]> git.ipfire.org Git - people/ms/libloc.git/blame_incremental - src/database.c
database: Map network nodes section when opening the database
[people/ms/libloc.git] / src / database.c
... / ...
CommitLineData
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
17#include <endian.h>
18#include <errno.h>
19#include <stddef.h>
20#include <stdint.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/mman.h>
25#include <sys/types.h>
26#include <time.h>
27#include <unistd.h>
28
29#include <loc/libloc.h>
30#include <loc/as.h>
31#include <loc/database.h>
32#include <loc/format.h>
33#include <loc/private.h>
34#include <loc/stringpool.h>
35
36struct loc_database {
37 struct loc_ctx* ctx;
38 int refcount;
39
40 unsigned int version;
41 time_t created_at;
42 off_t vendor;
43 off_t description;
44
45 // ASes in the database
46 struct loc_database_as_v0* as_v0;
47 size_t as_count;
48
49 // Network tree
50 struct loc_database_network_node_v0* network_nodes_v0;
51 size_t network_nodes_count;
52
53 struct loc_stringpool* pool;
54};
55
56static int loc_database_read_magic(struct loc_database* db, FILE* f) {
57 struct loc_database_magic magic;
58
59 // Read from file
60 size_t bytes_read = fread(&magic, 1, sizeof(magic), f);
61
62 // Check if we have been able to read enough data
63 if (bytes_read < sizeof(magic)) {
64 ERROR(db->ctx, "Could not read enough data to validate magic bytes\n");
65 DEBUG(db->ctx, "Read %zu bytes, but needed %zu\n", bytes_read, sizeof(magic));
66 return -ENOMSG;
67 }
68
69 // Compare magic bytes
70 if (memcmp(LOC_DATABASE_MAGIC, magic.magic, strlen(LOC_DATABASE_MAGIC)) == 0) {
71 DEBUG(db->ctx, "Magic value matches\n");
72
73 // Parse version
74 db->version = be16toh(magic.version);
75 DEBUG(db->ctx, "Database version is %u\n", db->version);
76
77 return 0;
78 }
79
80 ERROR(db->ctx, "Database format is not compatible\n");
81
82 // Return an error
83 return 1;
84}
85
86static int loc_database_read_as_section_v0(struct loc_database* db,
87 FILE* f, off_t as_offset, size_t as_length) {
88 DEBUG(db->ctx, "Reading AS section from %jd (%zu bytes)\n", as_offset, as_length);
89
90 if (as_length > 0) {
91 db->as_v0 = mmap(NULL, as_length, PROT_READ,
92 MAP_SHARED, fileno(f), as_offset);
93
94 if (db->as_v0 == MAP_FAILED)
95 return -errno;
96 }
97
98 db->as_count = as_length / sizeof(*db->as_v0);
99
100 INFO(db->ctx, "Read %zu ASes from the database\n", db->as_count);
101
102 return 0;
103}
104
105static int loc_database_read_network_nodes_section_v0(struct loc_database* db,
106 FILE* f, off_t network_nodes_offset, size_t network_nodes_length) {
107 DEBUG(db->ctx, "Reading network nodes section from %jd (%zu bytes)\n",
108 network_nodes_offset, network_nodes_length);
109
110 if (network_nodes_length > 0) {
111 db->network_nodes_v0 = mmap(NULL, network_nodes_length, PROT_READ,
112 MAP_SHARED, fileno(f), network_nodes_offset);
113
114 if (db->network_nodes_v0 == MAP_FAILED)
115 return -errno;
116 }
117
118 db->network_nodes_count = network_nodes_length / sizeof(*db->network_nodes_v0);
119
120 INFO(db->ctx, "Read %zu network nodes from the database\n", db->network_nodes_count);
121
122 return 0;
123}
124
125static int loc_database_read_header_v0(struct loc_database* db, FILE* f) {
126 struct loc_database_header_v0 header;
127
128 // Read from file
129 size_t size = fread(&header, 1, sizeof(header), f);
130
131 if (size < sizeof(header)) {
132 ERROR(db->ctx, "Could not read enough data for header\n");
133 return -ENOMSG;
134 }
135
136 // Copy over data
137 db->created_at = be64toh(header.created_at);
138 db->vendor = be32toh(header.vendor);
139 db->description = be32toh(header.description);
140
141 // Open pool
142 off_t pool_offset = be32toh(header.pool_offset);
143 size_t pool_length = be32toh(header.pool_length);
144
145 int r = loc_stringpool_open(db->ctx, &db->pool,
146 f, pool_length, pool_offset);
147 if (r)
148 return r;
149
150 // AS section
151 off_t as_offset = be32toh(header.as_offset);
152 size_t as_length = be32toh(header.as_length);
153
154 r = loc_database_read_as_section_v0(db, f, as_offset, as_length);
155 if (r)
156 return r;
157
158 // Network Nodes
159 off_t network_nodes_offset = be32toh(header.network_tree_offset);
160 size_t network_nodes_length = be32toh(header.network_tree_length);
161
162 r = loc_database_read_network_nodes_section_v0(db, f,
163 network_nodes_offset, network_nodes_length);
164 if (r)
165 return r;
166
167 return 0;
168}
169
170static int loc_database_read_header(struct loc_database* db, FILE* f) {
171 switch (db->version) {
172 case 0:
173 return loc_database_read_header_v0(db, f);
174
175 default:
176 ERROR(db->ctx, "Incompatible database version: %u\n", db->version);
177 return 1;
178 }
179}
180
181static int loc_database_read(struct loc_database* db, FILE* f) {
182 clock_t start = clock();
183
184 // Read magic bytes
185 int r = loc_database_read_magic(db, f);
186 if (r)
187 return r;
188
189 // Read the header
190 r = loc_database_read_header(db, f);
191 if (r)
192 return r;
193
194 clock_t end = clock();
195
196 INFO(db->ctx, "Opened database in %.8fs\n",
197 (double)(end - start) / CLOCKS_PER_SEC);
198
199 return 0;
200}
201
202LOC_EXPORT int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE* f) {
203 // Fail on invalid file handle
204 if (!f)
205 return -EINVAL;
206
207 struct loc_database* db = calloc(1, sizeof(*db));
208 if (!db)
209 return -ENOMEM;
210
211 // Reference context
212 db->ctx = loc_ref(ctx);
213 db->refcount = 1;
214
215 DEBUG(db->ctx, "Database object allocated at %p\n", db);
216
217 int r = loc_database_read(db, f);
218 if (r) {
219 loc_database_unref(db);
220 return r;
221 }
222
223 *database = db;
224
225 return 0;
226}
227
228LOC_EXPORT struct loc_database* loc_database_ref(struct loc_database* db) {
229 db->refcount++;
230
231 return db;
232}
233
234static void loc_database_free(struct loc_database* db) {
235 DEBUG(db->ctx, "Releasing database %p\n", db);
236
237 // Removing all ASes
238 if (db->as_v0) {
239 int r = munmap(db->as_v0, db->as_count * sizeof(*db->as_v0));
240 if (r)
241 ERROR(db->ctx, "Could not unmap AS section: %s\n", strerror(errno));
242 }
243
244 loc_stringpool_unref(db->pool);
245
246 loc_unref(db->ctx);
247 free(db);
248}
249
250LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) {
251 if (--db->refcount > 0)
252 return NULL;
253
254 loc_database_free(db);
255 return NULL;
256}
257
258LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) {
259 return db->created_at;
260}
261
262LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) {
263 return loc_stringpool_get(db->pool, db->vendor);
264}
265
266LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) {
267 return loc_stringpool_get(db->pool, db->description);
268}
269
270LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) {
271 return db->as_count;
272}
273
274// Returns the AS at position pos
275static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, off_t pos) {
276 if ((size_t)pos >= db->as_count)
277 return -EINVAL;
278
279 DEBUG(db->ctx, "Fetching AS at position %jd\n", pos);
280
281 int r;
282 switch (db->version) {
283 case 0:
284 r = loc_as_new_from_database_v0(db->ctx, db->pool, as, db->as_v0 + pos);
285 break;
286
287 default:
288 return -1;
289 }
290
291 if (r == 0) {
292 DEBUG(db->ctx, "Got AS%u\n", loc_as_get_number(*as));
293 }
294
295 return r;
296}
297
298// Performs a binary search to find the AS in the list
299LOC_EXPORT int loc_database_get_as(struct loc_database* db, struct loc_as** as, uint32_t number) {
300 off_t lo = 0;
301 off_t hi = db->as_count - 1;
302
303 // Save start time
304 clock_t start = clock();
305
306 while (lo <= hi) {
307 off_t i = (lo + hi) / 2;
308
309 // Fetch AS in the middle between lo and hi
310 int r = loc_database_fetch_as(db, as, i);
311 if (r)
312 return r;
313
314 // Check if this is a match
315 uint32_t as_number = loc_as_get_number(*as);
316 if (as_number == number) {
317 clock_t end = clock();
318
319 // Log how fast this has been
320 DEBUG(db->ctx, "Found AS%u in %.8fs\n", as_number,
321 (double)(end - start) / CLOCKS_PER_SEC);
322
323 return 0;
324 }
325
326 // If it wasn't, we release the AS and
327 // adjust our search pointers
328 loc_as_unref(*as);
329
330 if (as_number < number) {
331 lo = i + 1;
332 } else
333 hi = i - 1;
334 }
335
336 // Nothing found
337 *as = NULL;
338
339 return 1;
340}