]> git.ipfire.org Git - people/ms/libloc.git/blame - src/database.c
Make package compile on Mac OS X
[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
c182393f 139 DEBUG(db->ctx, "Reading AS section from %jd (%zu bytes)\n", 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
MT
161 DEBUG(db->ctx, "Reading network nodes section from %jd (%zu bytes)\n",
162 network_nodes_offset, network_nodes_length);
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",
185 networks_offset, networks_length);
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",
208 countries_offset, countries_length);
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
c182393f 404 DEBUG(db->ctx, "Fetching AS at position %jd\n", 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) {
10778041
MT
470 if ((size_t)pos >= db->networks_count)
471 return -EINVAL;
472
473 DEBUG(db->ctx, "Fetching network at position %jd\n", pos);
474
475 int r;
476 switch (db->version) {
477 case 0:
39a55353
MT
478 r = loc_network_new_from_database_v0(db->ctx, network,
479 address, prefix, db->networks_v0 + pos);
10778041
MT
480 break;
481
482 default:
483 return -1;
484 }
485
486 if (r == 0) {
487 char* string = loc_network_str(*network);
488 DEBUG(db->ctx, "Got network %s\n", string);
489 free(string);
490 }
491
492 return r;
493}
2a30e4de 494
025ef489 495static int __loc_database_node_is_leaf(const struct loc_database_network_node_v0* node) {
39a55353 496 return (node->network != htobe32(0xffffffff));
025ef489
MT
497}
498
499static int __loc_database_lookup_handle_leaf(struct loc_database* db, const struct in6_addr* address,
39a55353 500 struct loc_network** network, struct in6_addr* network_address, unsigned int prefix,
2a30e4de 501 const struct loc_database_network_node_v0* node) {
39a55353
MT
502 off_t network_index = be32toh(node->network);
503
504 DEBUG(db->ctx, "Handling leaf node at %jd (%jd)\n", node - db->network_nodes_v0, network_index);
2a30e4de
MT
505
506 // Fetch the network
507 int r = loc_database_fetch_network(db, network,
39a55353 508 network_address, prefix, network_index);
e85e2b0b
MT
509 if (r) {
510 ERROR(db->ctx, "Could not fetch network %jd from database\n", network_index);
2a30e4de 511 return r;
e85e2b0b 512 }
39a55353 513
2a30e4de
MT
514 // Check if the given IP address is inside the network
515 r = loc_network_match_address(*network, address);
516 if (r) {
517 DEBUG(db->ctx, "Searched address is not part of the network\n");
518
519 loc_network_unref(*network);
520 *network = NULL;
521 return 1;
522 }
523
524 // A network was found and the IP address matches
525 return 0;
526}
527
2a30e4de
MT
528// Searches for an exact match along the path
529static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address,
530 struct loc_network** network, struct in6_addr* network_address,
f66f15e1 531 const struct loc_database_network_node_v0* node, unsigned int level) {
025ef489 532 int r;
2a30e4de
MT
533 off_t node_index;
534
535 // Follow the path
536 int bit = in6_addr_get_bit(address, level);
537 in6_addr_set_bit(network_address, level, bit);
538
539 if (bit == 0)
540 node_index = be32toh(node->zero);
541 else
542 node_index = be32toh(node->one);
543
9086d2b1
MT
544 // If the node index is zero, the tree ends here
545 // and we cannot descend any further
546 if (node_index > 0) {
547 // Check boundaries
548 if ((size_t)node_index >= db->network_nodes_count)
549 return -EINVAL;
2a30e4de 550
9086d2b1
MT
551 // Move on to the next node
552 r = __loc_database_lookup(db, address, network, network_address,
553 db->network_nodes_v0 + node_index, level + 1);
2a30e4de 554
9086d2b1
MT
555 // End here if a result was found
556 if (r == 0)
557 return r;
2a30e4de 558
9086d2b1
MT
559 // Raise any errors
560 else if (r < 0)
561 return r;
ec1d9681
MT
562
563 DEBUG(db->ctx, "No match found below level %u\n", level);
564 } else {
565 DEBUG(db->ctx, "Tree ended at level %u\n", level);
9086d2b1 566 }
2a30e4de 567
9086d2b1
MT
568 // If this node has a leaf, we will check if it matches
569 if (__loc_database_node_is_leaf(node)) {
570 r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, node);
571 if (r <= 0)
572 return r;
573 }
2a30e4de 574
ec1d9681 575 return 1;
2a30e4de
MT
576}
577
578LOC_EXPORT int loc_database_lookup(struct loc_database* db,
579 struct in6_addr* address, struct loc_network** network) {
580 struct in6_addr network_address;
581 memset(&network_address, 0, sizeof(network_address));
582
583 *network = NULL;
584
585 // Save start time
586 clock_t start = clock();
587
588 int r = __loc_database_lookup(db, address, network, &network_address,
589 db->network_nodes_v0, 0);
590
591 clock_t end = clock();
592
593 // Log how fast this has been
e16c943b
MT
594 DEBUG(db->ctx, "Executed network search in %.4fms\n",
595 (double)(end - start) / CLOCKS_PER_SEC * 1000);
2a30e4de
MT
596
597 return r;
598}
599
600LOC_EXPORT int loc_database_lookup_from_string(struct loc_database* db,
601 const char* string, struct loc_network** network) {
602 struct in6_addr address;
603
604 int r = loc_parse_address(db->ctx, string, &address);
605 if (r)
606 return r;
607
608 return loc_database_lookup(db, &address, network);
609}
7e13db74 610
ec684c1a
MT
611// Returns the country at position pos
612static int loc_database_fetch_country(struct loc_database* db,
613 struct loc_country** country, off_t pos) {
614 if ((size_t)pos >= db->countries_count)
615 return -EINVAL;
616
617 DEBUG(db->ctx, "Fetching country at position %jd\n", pos);
618
619 int r;
620 switch (db->version) {
621 case 0:
622 r = loc_country_new_from_database_v0(db->ctx, db->pool, country, db->countries_v0 + pos);
623 break;
624
625 default:
626 return -1;
627 }
628
629 if (r == 0) {
630 DEBUG(db->ctx, "Got country %s\n", loc_country_get_code(*country));
631 }
632
633 return r;
634}
635
636// Performs a binary search to find the country in the list
637LOC_EXPORT int loc_database_get_country(struct loc_database* db,
638 struct loc_country** country, const char* code) {
639 off_t lo = 0;
640 off_t hi = db->countries_count - 1;
641
642 // Save start time
643 clock_t start = clock();
644
645 while (lo <= hi) {
646 off_t i = (lo + hi) / 2;
647
648 // Fetch country in the middle between lo and hi
649 int r = loc_database_fetch_country(db, country, i);
650 if (r)
651 return r;
652
653 // Check if this is a match
654 const char* cc = loc_country_get_code(*country);
655 int result = strcmp(code, cc);
656
657 if (result == 0) {
658 clock_t end = clock();
659
660 // Log how fast this has been
661 DEBUG(db->ctx, "Found country %s in %.4fms\n", cc,
662 (double)(end - start) / CLOCKS_PER_SEC * 1000);
663
664 return 0;
665 }
666
667 // If it wasn't, we release the country and
668 // adjust our search pointers
669 loc_country_unref(*country);
670
671 if (result < 0) {
672 lo = i + 1;
673 } else
674 hi = i - 1;
675 }
676
677 // Nothing found
678 *country = NULL;
679
680 return 1;
681}
682
7e13db74
MT
683// Enumerator
684
ccc7ab4e
MT
685LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
686 struct loc_database* db, enum loc_database_enumerator_mode mode) {
7e13db74
MT
687 struct loc_database_enumerator* e = calloc(1, sizeof(*e));
688 if (!e)
689 return -ENOMEM;
690
691 // Reference context
692 e->ctx = loc_ref(db->ctx);
693 e->db = loc_database_ref(db);
ccc7ab4e 694 e->mode = mode;
7e13db74
MT
695 e->refcount = 1;
696
e3f696c1
MT
697 // Initialise graph search
698 //e->network_stack[++e->network_stack_depth] = 0;
699 e->network_stack_depth = 1;
700 e->networks_visited = calloc(db->network_nodes_count, sizeof(*e->networks_visited));
701
7e13db74
MT
702 DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
703
704 *enumerator = e;
705 return 0;
706}
707
708LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator) {
709 enumerator->refcount++;
710
711 return enumerator;
712}
713
714static void loc_database_enumerator_free(struct loc_database_enumerator* enumerator) {
715 DEBUG(enumerator->ctx, "Releasing database enumerator %p\n", enumerator);
716
717 // Release all references
718 loc_database_unref(enumerator->db);
719 loc_unref(enumerator->ctx);
720
d3d8ede6
MT
721 if (enumerator->string)
722 free(enumerator->string);
723
91d89020
MT
724 // Free network search
725 free(enumerator->networks_visited);
726
7e13db74
MT
727 free(enumerator);
728}
729
730LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator) {
731 if (!enumerator)
732 return NULL;
733
734 if (--enumerator->refcount > 0)
735 return enumerator;
736
737 loc_database_enumerator_free(enumerator);
738 return NULL;
739}
d3d8ede6
MT
740
741LOC_EXPORT int loc_database_enumerator_set_string(struct loc_database_enumerator* enumerator, const char* string) {
742 enumerator->string = strdup(string);
743
744 // Make the string lowercase
745 for (char *p = enumerator->string; *p; p++)
746 *p = tolower(*p);
747
748 return 0;
749}
750
35bb3a32
MT
751LOC_EXPORT int loc_database_enumerator_set_country_code(struct loc_database_enumerator* enumerator, const char* country_code) {
752 // Set empty country code
753 if (!country_code || !*country_code) {
754 *enumerator->country_code = '\0';
755 return 0;
756 }
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
57146963
MT
771 // Country codes must be two characters
772 if (!loc_country_code_is_valid(country_code))
773 return -EINVAL;
774
35bb3a32
MT
775 for (unsigned int i = 0; i < 3; i++) {
776 enumerator->country_code[i] = country_code[i];
777 }
778
779 return 0;
780}
781
82910b95
MT
782LOC_EXPORT int loc_database_enumerator_set_asn(
783 struct loc_database_enumerator* enumerator, unsigned int asn) {
784 enumerator->asn = asn;
785
786 return 0;
787}
788
9268db5a
MT
789LOC_EXPORT int loc_database_enumerator_set_flag(
790 struct loc_database_enumerator* enumerator, enum loc_network_flags flag) {
791 enumerator->flags |= flag;
792
793 return 0;
794}
795
15f79e2d
MT
796LOC_EXPORT int loc_database_enumerator_next_as(
797 struct loc_database_enumerator* enumerator, struct loc_as** as) {
798 *as = NULL;
799
ccc7ab4e
MT
800 // Do not do anything if not in AS mode
801 if (enumerator->mode != LOC_DB_ENUMERATE_ASES)
15f79e2d 802 return 0;
ccc7ab4e 803
d3d8ede6 804 struct loc_database* db = enumerator->db;
d3d8ede6
MT
805
806 while (enumerator->as_index < db->as_count) {
807 // Fetch the next AS
15f79e2d 808 int r = loc_database_fetch_as(db, as, enumerator->as_index++);
d3d8ede6 809 if (r)
15f79e2d 810 return r;
d3d8ede6 811
15f79e2d 812 r = loc_as_match_string(*as, enumerator->string);
273948cf 813 if (r == 1) {
d3d8ede6 814 DEBUG(enumerator->ctx, "AS%d (%s) matches %s\n",
15f79e2d 815 loc_as_get_number(*as), loc_as_get_name(*as), enumerator->string);
d3d8ede6 816
15f79e2d 817 return 0;
d3d8ede6
MT
818 }
819
820 // No match
15f79e2d 821 loc_as_unref(*as);
74f218f0 822 *as = NULL;
d3d8ede6
MT
823 }
824
825 // Reset the index
826 enumerator->as_index = 0;
827
828 // We have searched through all of them
15f79e2d 829 return 0;
d3d8ede6 830}
e3f696c1
MT
831
832static int loc_database_enumerator_stack_push_node(
833 struct loc_database_enumerator* e, off_t offset, int i, int depth) {
834 // Do not add empty nodes
835 if (!offset)
836 return 0;
837
838 // Check if there is any space left on the stack
839 if (e->network_stack_depth >= MAX_STACK_DEPTH) {
840 ERROR(e->ctx, "Maximum stack size reached: %d\n", e->network_stack_depth);
841 return -1;
842 }
843
844 // Increase stack size
845 int s = ++e->network_stack_depth;
846
847 DEBUG(e->ctx, "Added node %jd to stack (%d)\n", offset, depth);
848
849 e->network_stack[s].offset = offset;
850 e->network_stack[s].i = i;
851 e->network_stack[s].depth = depth;
852
853 return 0;
854}
855
15f79e2d
MT
856LOC_EXPORT int loc_database_enumerator_next_network(
857 struct loc_database_enumerator* enumerator, struct loc_network** network) {
e3f696c1
MT
858 // Reset network
859 *network = NULL;
15f79e2d
MT
860
861 // Do not do anything if not in network mode
862 if (enumerator->mode != LOC_DB_ENUMERATE_NETWORKS)
863 return 0;
864
e3f696c1
MT
865 int r;
866
15f79e2d
MT
867 DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
868 enumerator->network_stack_depth);
e3f696c1
MT
869
870 // Perform DFS
15f79e2d
MT
871 while (enumerator->network_stack_depth > 0) {
872 DEBUG(enumerator->ctx, "Stack depth: %u\n", enumerator->network_stack_depth);
e3f696c1
MT
873
874 // Get object from top of the stack
15f79e2d 875 struct loc_node_stack* node = &enumerator->network_stack[enumerator->network_stack_depth];
e3f696c1
MT
876
877 // Remove the node from the stack if we have already visited it
15f79e2d
MT
878 if (enumerator->networks_visited[node->offset]) {
879 enumerator->network_stack_depth--;
e3f696c1
MT
880 continue;
881 }
882
74fb733a 883 // Mark the bits on the path correctly
15f79e2d 884 in6_addr_set_bit(&enumerator->network_address,
e3f696c1
MT
885 (node->depth > 0) ? node->depth - 1 : 0, node->i);
886
15f79e2d
MT
887 DEBUG(enumerator->ctx, "Looking at node %jd\n", node->offset);
888 enumerator->networks_visited[node->offset]++;
e3f696c1
MT
889
890 // Pop node from top of the stack
891 struct loc_database_network_node_v0* n =
15f79e2d 892 enumerator->db->network_nodes_v0 + node->offset;
e3f696c1
MT
893
894 // Add edges to stack
15f79e2d 895 r = loc_database_enumerator_stack_push_node(enumerator,
e3f696c1
MT
896 be32toh(n->one), 1, node->depth + 1);
897
898 if (r)
899 return r;
900
15f79e2d 901 r = loc_database_enumerator_stack_push_node(enumerator,
e3f696c1
MT
902 be32toh(n->zero), 0, node->depth + 1);
903
904 if (r)
905 return r;
906
907 // Check if this node is a leaf and has a network object
908 if (__loc_database_node_is_leaf(n)) {
909 off_t network_index = be32toh(n->network);
910
15f79e2d 911 DEBUG(enumerator->ctx, "Node has a network at %jd\n", network_index);
e3f696c1
MT
912
913 // Fetch the network object
15f79e2d
MT
914 r = loc_database_fetch_network(enumerator->db, network,
915 &enumerator->network_address, node->depth, network_index);
e3f696c1
MT
916
917 // Break on any errors
918 if (r)
919 return r;
920
921 // Check if we are interested in this network
922
923 // Skip if the country code does not match
15f79e2d
MT
924 if (enumerator->country_code &&
925 !loc_network_match_country_code(*network, enumerator->country_code)) {
e3f696c1 926 loc_network_unref(*network);
15f79e2d
MT
927 *network = NULL;
928
e3f696c1
MT
929 continue;
930 }
931
82910b95 932 // Skip if the ASN does not match
15f79e2d
MT
933 if (enumerator->asn &&
934 !loc_network_match_asn(*network, enumerator->asn)) {
82910b95 935 loc_network_unref(*network);
15f79e2d
MT
936 *network = NULL;
937
82910b95
MT
938 continue;
939 }
940
9268db5a
MT
941 // Skip if flags do not match
942 if (enumerator->flags &&
943 !loc_network_match_flag(*network, enumerator->flags)) {
944 loc_network_unref(*network);
945 *network = NULL;
946 }
947
e3f696c1
MT
948 return 0;
949 }
950 }
951
952 // Reached the end of the search
fe483cdc
MT
953
954 // Mark all nodes as non-visited
15f79e2d
MT
955 for (unsigned int i = 0; i < enumerator->db->network_nodes_count; i++)
956 enumerator->networks_visited[i] = 0;
e3f696c1
MT
957
958 return 0;
959}