database: Map network nodes section when opening the database
[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
0676cd80 17#include <endian.h>
2601e83e
MT
18#include <errno.h>
19#include <stddef.h>
20#include <stdint.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
c182393f 24#include <sys/mman.h>
2601e83e 25#include <sys/types.h>
96ea74a5 26#include <time.h>
3f35869a 27#include <unistd.h>
2601e83e
MT
28
29#include <loc/libloc.h>
9fc7f001
MT
30#include <loc/as.h>
31#include <loc/database.h>
a5db3e49 32#include <loc/format.h>
9fc7f001
MT
33#include <loc/private.h>
34#include <loc/stringpool.h>
2601e83e
MT
35
36struct loc_database {
37 struct loc_ctx* ctx;
38 int refcount;
39
40 unsigned int version;
96ea74a5 41 time_t created_at;
2601e83e
MT
42 off_t vendor;
43 off_t description;
44
a5db3e49 45 // ASes in the database
c182393f 46 struct loc_database_as_v0* as_v0;
a5db3e49
MT
47 size_t as_count;
48
f66b7b09
MT
49 // Network tree
50 struct loc_database_network_node_v0* network_nodes_v0;
51 size_t network_nodes_count;
52
2601e83e
MT
53 struct loc_stringpool* pool;
54};
55
a7431f1a 56static int loc_database_read_magic(struct loc_database* db, FILE* f) {
2601e83e
MT
57 struct loc_database_magic magic;
58
59 // Read from file
a7431f1a 60 size_t bytes_read = fread(&magic, 1, sizeof(magic), f);
2601e83e
MT
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
0676cd80 74 db->version = be16toh(magic.version);
2601e83e
MT
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
a5db3e49 86static int loc_database_read_as_section_v0(struct loc_database* db,
a7431f1a 87 FILE* f, off_t as_offset, size_t as_length) {
c182393f 88 DEBUG(db->ctx, "Reading AS section from %jd (%zu bytes)\n", as_offset, as_length);
a5db3e49 89
c182393f
MT
90 if (as_length > 0) {
91 db->as_v0 = mmap(NULL, as_length, PROT_READ,
a7431f1a 92 MAP_SHARED, fileno(f), as_offset);
a5db3e49 93
c182393f
MT
94 if (db->as_v0 == MAP_FAILED)
95 return -errno;
a5db3e49
MT
96 }
97
c182393f
MT
98 db->as_count = as_length / sizeof(*db->as_v0);
99
a5db3e49
MT
100 INFO(db->ctx, "Read %zu ASes from the database\n", db->as_count);
101
102 return 0;
103}
104
f66b7b09
MT
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
a7431f1a 125static int loc_database_read_header_v0(struct loc_database* db, FILE* f) {
2601e83e
MT
126 struct loc_database_header_v0 header;
127
128 // Read from file
a7431f1a 129 size_t size = fread(&header, 1, sizeof(header), f);
2601e83e
MT
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
96ea74a5 137 db->created_at = be64toh(header.created_at);
0676cd80
MT
138 db->vendor = be32toh(header.vendor);
139 db->description = be32toh(header.description);
2601e83e
MT
140
141 // Open pool
0676cd80
MT
142 off_t pool_offset = be32toh(header.pool_offset);
143 size_t pool_length = be32toh(header.pool_length);
2601e83e 144
0e974d4b 145 int r = loc_stringpool_open(db->ctx, &db->pool,
a7431f1a 146 f, pool_length, pool_offset);
2601e83e
MT
147 if (r)
148 return r;
149
a5db3e49 150 // AS section
0676cd80
MT
151 off_t as_offset = be32toh(header.as_offset);
152 size_t as_length = be32toh(header.as_length);
a5db3e49 153
a7431f1a 154 r = loc_database_read_as_section_v0(db, f, as_offset, as_length);
a5db3e49
MT
155 if (r)
156 return r;
157
f66b7b09
MT
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
2601e83e
MT
167 return 0;
168}
169
a7431f1a 170static int loc_database_read_header(struct loc_database* db, FILE* f) {
2601e83e
MT
171 switch (db->version) {
172 case 0:
a7431f1a 173 return loc_database_read_header_v0(db, f);
2601e83e
MT
174
175 default:
176 ERROR(db->ctx, "Incompatible database version: %u\n", db->version);
177 return 1;
178 }
179}
180
a7431f1a 181static int loc_database_read(struct loc_database* db, FILE* f) {
02879100
MT
182 clock_t start = clock();
183
184 // Read magic bytes
a7431f1a 185 int r = loc_database_read_magic(db, f);
02879100
MT
186 if (r)
187 return r;
188
189 // Read the header
a7431f1a 190 r = loc_database_read_header(db, f);
02879100
MT
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
c182393f 202LOC_EXPORT int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE* f) {
a7431f1a
MT
203 // Fail on invalid file handle
204 if (!f)
205 return -EINVAL;
206
c182393f
MT
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
a7431f1a 217 int r = loc_database_read(db, f);
02879100
MT
218 if (r) {
219 loc_database_unref(db);
2601e83e 220 return r;
02879100 221 }
2601e83e 222
c182393f
MT
223 *database = db;
224
2601e83e 225 return 0;
2601e83e
MT
226}
227
c182393f
MT
228LOC_EXPORT struct loc_database* loc_database_ref(struct loc_database* db) {
229 db->refcount++;
230
231 return db;
8f5b676a
MT
232}
233
c182393f
MT
234static void loc_database_free(struct loc_database* db) {
235 DEBUG(db->ctx, "Releasing database %p\n", db);
c34e76f1 236
c182393f
MT
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 }
c34e76f1 243
c182393f 244 loc_stringpool_unref(db->pool);
c34e76f1 245
c182393f
MT
246 loc_unref(db->ctx);
247 free(db);
c34e76f1
MT
248}
249
c182393f
MT
250LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) {
251 if (--db->refcount > 0)
252 return NULL;
78ace4ed 253
c182393f
MT
254 loc_database_free(db);
255 return NULL;
256}
78ace4ed 257
c182393f
MT
258LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) {
259 return db->created_at;
260}
78ace4ed 261
c182393f
MT
262LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) {
263 return loc_stringpool_get(db->pool, db->vendor);
264}
78ace4ed 265
c182393f
MT
266LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) {
267 return loc_stringpool_get(db->pool, db->description);
268}
78ace4ed 269
c182393f
MT
270LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) {
271 return db->as_count;
78ace4ed
MT
272}
273
c182393f
MT
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;
2601e83e 278
c182393f 279 DEBUG(db->ctx, "Fetching AS at position %jd\n", pos);
2601e83e
MT
280
281 int r;
c182393f
MT
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;
2601e83e 286
c182393f
MT
287 default:
288 return -1;
289 }
2601e83e 290
c182393f
MT
291 if (r == 0) {
292 DEBUG(db->ctx, "Got AS%u\n", loc_as_get_number(*as));
2601e83e 293 }
2601e83e 294
c182393f
MT
295 return r;
296}
c34e76f1 297
c182393f
MT
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;
c34e76f1 302
8f3e2a06
MT
303 // Save start time
304 clock_t start = clock();
305
c182393f
MT
306 while (lo <= hi) {
307 off_t i = (lo + hi) / 2;
8f5b676a 308
c182393f
MT
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;
a5db3e49 313
c182393f
MT
314 // Check if this is a match
315 uint32_t as_number = loc_as_get_number(*as);
8f3e2a06
MT
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
c182393f 323 return 0;
8f3e2a06 324 }
c182393f
MT
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 }
2601e83e 335
c182393f
MT
336 // Nothing found
337 *as = NULL;
2601e83e 338
8f3e2a06 339 return 1;
2601e83e 340}