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