2 libloc - A library to determine the location of someone on the Internet
4 Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
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.
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.
19 #include <netinet/in.h>
26 #include <sys/types.h>
30 #include <loc/libloc.h>
32 #include <loc/database.h>
33 #include <loc/format.h>
34 #include <loc/network.h>
35 #include <loc/private.h>
36 #include <loc/stringpool.h>
47 // ASes in the database
48 struct loc_database_as_v0
* as_v0
;
52 struct loc_database_network_node_v0
* network_nodes_v0
;
53 size_t network_nodes_count
;
56 struct loc_database_network_v0
* networks_v0
;
57 size_t networks_count
;
59 struct loc_stringpool
* pool
;
62 static int loc_database_read_magic(struct loc_database
* db
, FILE* f
) {
63 struct loc_database_magic magic
;
66 size_t bytes_read
= fread(&magic
, 1, sizeof(magic
), f
);
68 // Check if we have been able to read enough data
69 if (bytes_read
< sizeof(magic
)) {
70 ERROR(db
->ctx
, "Could not read enough data to validate magic bytes\n");
71 DEBUG(db
->ctx
, "Read %zu bytes, but needed %zu\n", bytes_read
, sizeof(magic
));
75 // Compare magic bytes
76 if (memcmp(LOC_DATABASE_MAGIC
, magic
.magic
, strlen(LOC_DATABASE_MAGIC
)) == 0) {
77 DEBUG(db
->ctx
, "Magic value matches\n");
80 db
->version
= be16toh(magic
.version
);
81 DEBUG(db
->ctx
, "Database version is %u\n", db
->version
);
86 ERROR(db
->ctx
, "Database format is not compatible\n");
92 static int loc_database_read_as_section_v0(struct loc_database
* db
,
93 FILE* f
, const struct loc_database_header_v0
* header
) {
94 off_t as_offset
= be32toh(header
->as_offset
);
95 size_t as_length
= be32toh(header
->as_length
);
97 DEBUG(db
->ctx
, "Reading AS section from %jd (%zu bytes)\n", as_offset
, as_length
);
100 db
->as_v0
= mmap(NULL
, as_length
, PROT_READ
,
101 MAP_SHARED
, fileno(f
), as_offset
);
103 if (db
->as_v0
== MAP_FAILED
)
107 db
->as_count
= as_length
/ sizeof(*db
->as_v0
);
109 INFO(db
->ctx
, "Read %zu ASes from the database\n", db
->as_count
);
114 static int loc_database_read_network_nodes_section_v0(struct loc_database
* db
,
115 FILE* f
, const struct loc_database_header_v0
* header
) {
116 off_t network_nodes_offset
= be32toh(header
->network_tree_offset
);
117 size_t network_nodes_length
= be32toh(header
->network_tree_length
);
119 DEBUG(db
->ctx
, "Reading network nodes section from %jd (%zu bytes)\n",
120 network_nodes_offset
, network_nodes_length
);
122 if (network_nodes_length
> 0) {
123 db
->network_nodes_v0
= mmap(NULL
, network_nodes_length
, PROT_READ
,
124 MAP_SHARED
, fileno(f
), network_nodes_offset
);
126 if (db
->network_nodes_v0
== MAP_FAILED
)
130 db
->network_nodes_count
= network_nodes_length
/ sizeof(*db
->network_nodes_v0
);
132 INFO(db
->ctx
, "Read %zu network nodes from the database\n", db
->network_nodes_count
);
137 static int loc_database_read_networks_section_v0(struct loc_database
* db
,
138 FILE* f
, const struct loc_database_header_v0
* header
) {
139 off_t networks_offset
= be32toh(header
->network_data_offset
);
140 size_t networks_length
= be32toh(header
->network_data_length
);
142 DEBUG(db
->ctx
, "Reading networks section from %jd (%zu bytes)\n",
143 networks_offset
, networks_length
);
145 if (networks_length
> 0) {
146 db
->networks_v0
= mmap(NULL
, networks_length
, PROT_READ
,
147 MAP_SHARED
, fileno(f
), networks_offset
);
149 if (db
->networks_v0
== MAP_FAILED
)
153 db
->networks_count
= networks_length
/ sizeof(*db
->networks_v0
);
155 INFO(db
->ctx
, "Read %zu networks from the database\n", db
->networks_count
);
160 static int loc_database_read_header_v0(struct loc_database
* db
, FILE* f
) {
161 struct loc_database_header_v0 header
;
164 size_t size
= fread(&header
, 1, sizeof(header
), f
);
166 if (size
< sizeof(header
)) {
167 ERROR(db
->ctx
, "Could not read enough data for header\n");
172 db
->created_at
= be64toh(header
.created_at
);
173 db
->vendor
= be32toh(header
.vendor
);
174 db
->description
= be32toh(header
.description
);
177 off_t pool_offset
= be32toh(header
.pool_offset
);
178 size_t pool_length
= be32toh(header
.pool_length
);
180 int r
= loc_stringpool_open(db
->ctx
, &db
->pool
,
181 f
, pool_length
, pool_offset
);
186 r
= loc_database_read_as_section_v0(db
, f
, &header
);
191 r
= loc_database_read_network_nodes_section_v0(db
, f
, &header
);
196 r
= loc_database_read_networks_section_v0(db
, f
, &header
);
203 static int loc_database_read_header(struct loc_database
* db
, FILE* f
) {
204 switch (db
->version
) {
206 return loc_database_read_header_v0(db
, f
);
209 ERROR(db
->ctx
, "Incompatible database version: %u\n", db
->version
);
214 static int loc_database_read(struct loc_database
* db
, FILE* f
) {
215 clock_t start
= clock();
218 int r
= loc_database_read_magic(db
, f
);
223 r
= loc_database_read_header(db
, f
);
227 clock_t end
= clock();
229 INFO(db
->ctx
, "Opened database in %.8fs\n",
230 (double)(end
- start
) / CLOCKS_PER_SEC
);
235 LOC_EXPORT
int loc_database_new(struct loc_ctx
* ctx
, struct loc_database
** database
, FILE* f
) {
236 // Fail on invalid file handle
240 struct loc_database
* db
= calloc(1, sizeof(*db
));
245 db
->ctx
= loc_ref(ctx
);
248 DEBUG(db
->ctx
, "Database object allocated at %p\n", db
);
250 int r
= loc_database_read(db
, f
);
252 loc_database_unref(db
);
261 LOC_EXPORT
struct loc_database
* loc_database_ref(struct loc_database
* db
) {
267 static void loc_database_free(struct loc_database
* db
) {
268 DEBUG(db
->ctx
, "Releasing database %p\n", db
);
272 int r
= munmap(db
->as_v0
, db
->as_count
* sizeof(*db
->as_v0
));
274 ERROR(db
->ctx
, "Could not unmap AS section: %s\n", strerror(errno
));
277 loc_stringpool_unref(db
->pool
);
283 LOC_EXPORT
struct loc_database
* loc_database_unref(struct loc_database
* db
) {
284 if (--db
->refcount
> 0)
287 loc_database_free(db
);
291 LOC_EXPORT
time_t loc_database_created_at(struct loc_database
* db
) {
292 return db
->created_at
;
295 LOC_EXPORT
const char* loc_database_get_vendor(struct loc_database
* db
) {
296 return loc_stringpool_get(db
->pool
, db
->vendor
);
299 LOC_EXPORT
const char* loc_database_get_description(struct loc_database
* db
) {
300 return loc_stringpool_get(db
->pool
, db
->description
);
303 LOC_EXPORT
size_t loc_database_count_as(struct loc_database
* db
) {
307 // Returns the AS at position pos
308 static int loc_database_fetch_as(struct loc_database
* db
, struct loc_as
** as
, off_t pos
) {
309 if ((size_t)pos
>= db
->as_count
)
312 DEBUG(db
->ctx
, "Fetching AS at position %jd\n", pos
);
315 switch (db
->version
) {
317 r
= loc_as_new_from_database_v0(db
->ctx
, db
->pool
, as
, db
->as_v0
+ pos
);
325 DEBUG(db
->ctx
, "Got AS%u\n", loc_as_get_number(*as
));
331 // Performs a binary search to find the AS in the list
332 LOC_EXPORT
int loc_database_get_as(struct loc_database
* db
, struct loc_as
** as
, uint32_t number
) {
334 off_t hi
= db
->as_count
- 1;
337 clock_t start
= clock();
340 off_t i
= (lo
+ hi
) / 2;
342 // Fetch AS in the middle between lo and hi
343 int r
= loc_database_fetch_as(db
, as
, i
);
347 // Check if this is a match
348 uint32_t as_number
= loc_as_get_number(*as
);
349 if (as_number
== number
) {
350 clock_t end
= clock();
352 // Log how fast this has been
353 DEBUG(db
->ctx
, "Found AS%u in %.8fs\n", as_number
,
354 (double)(end
- start
) / CLOCKS_PER_SEC
);
359 // If it wasn't, we release the AS and
360 // adjust our search pointers
363 if (as_number
< number
) {
375 // Returns the network at position pos
376 static int loc_database_fetch_network(struct loc_database
* db
, struct loc_network
** network
, struct in6_addr
* address
, off_t pos
) {
377 if ((size_t)pos
>= db
->networks_count
)
380 DEBUG(db
->ctx
, "Fetching network at position %jd\n", pos
);
383 switch (db
->version
) {
385 r
= loc_network_new_from_database_v0(db
->ctx
, network
, address
, db
->networks_v0
+ pos
);
393 char* string
= loc_network_str(*network
);
394 DEBUG(db
->ctx
, "Got network %s\n", string
);