]> git.ipfire.org Git - people/ms/libloc.git/blame - src/writer.c
database: Load networks from database
[people/ms/libloc.git] / src / writer.c
CommitLineData
c182393f
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
c182393f
MT
17#include <endian.h>
18#include <errno.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
438db08c 22#include <sys/queue.h>
c182393f
MT
23#include <time.h>
24
9fc7f001
MT
25#include <loc/libloc.h>
26#include <loc/as.h>
c182393f 27#include <loc/format.h>
3b5f4af2 28#include <loc/network.h>
9fc7f001 29#include <loc/private.h>
c182393f
MT
30#include <loc/writer.h>
31
c182393f
MT
32struct loc_writer {
33 struct loc_ctx* ctx;
34 int refcount;
35
36 struct loc_stringpool* pool;
37 off_t vendor;
38 off_t description;
39
40 struct loc_as** as;
41 size_t as_count;
3b5f4af2
MT
42
43 struct loc_network_tree* networks;
c182393f
MT
44};
45
46LOC_EXPORT int loc_writer_new(struct loc_ctx* ctx, struct loc_writer** writer) {
47 struct loc_writer* w = calloc(1, sizeof(*w));
48 if (!w)
49 return -ENOMEM;
50
51 w->ctx = loc_ref(ctx);
52 w->refcount = 1;
53
0e974d4b 54 int r = loc_stringpool_new(ctx, &w->pool);
c182393f
MT
55 if (r) {
56 loc_writer_unref(w);
57 return r;
58 }
59
3b5f4af2
MT
60 // Initialize the network tree
61 r = loc_network_tree_new(ctx, &w->networks);
62 if (r) {
63 loc_writer_unref(w);
64 return r;
65 }
66
c182393f
MT
67 *writer = w;
68 return 0;
69}
70
71LOC_EXPORT struct loc_writer* loc_writer_ref(struct loc_writer* writer) {
72 writer->refcount++;
73
74 return writer;
75}
76
77static void loc_writer_free(struct loc_writer* writer) {
78 DEBUG(writer->ctx, "Releasing writer at %p\n", writer);
79
80 // Unref all AS
81 for (unsigned int i = 0; i < writer->as_count; i++) {
82 loc_as_unref(writer->as[i]);
83 }
84
3b5f4af2
MT
85 // Release network tree
86 if (writer->networks)
87 loc_network_tree_unref(writer->networks);
88
c182393f
MT
89 // Unref the string pool
90 loc_stringpool_unref(writer->pool);
91
92 loc_unref(writer->ctx);
93 free(writer);
94}
95
96LOC_EXPORT struct loc_writer* loc_writer_unref(struct loc_writer* writer) {
97 if (--writer->refcount > 0)
98 return writer;
99
100 loc_writer_free(writer);
101
102 return NULL;
103}
104
105LOC_EXPORT const char* loc_writer_get_vendor(struct loc_writer* writer) {
106 return loc_stringpool_get(writer->pool, writer->vendor);
107}
108
109LOC_EXPORT int loc_writer_set_vendor(struct loc_writer* writer, const char* vendor) {
110 // Add the string to the string pool
111 off_t offset = loc_stringpool_add(writer->pool, vendor);
112 if (offset < 0)
113 return offset;
114
115 writer->vendor = offset;
116 return 0;
117}
118
119LOC_EXPORT const char* loc_writer_get_description(struct loc_writer* writer) {
120 return loc_stringpool_get(writer->pool, writer->description);
121}
122
123LOC_EXPORT int loc_writer_set_description(struct loc_writer* writer, const char* description) {
124 // Add the string to the string pool
125 off_t offset = loc_stringpool_add(writer->pool, description);
126 if (offset < 0)
127 return offset;
128
129 writer->description = offset;
130 return 0;
131}
132
133static int __loc_as_cmp(const void* as1, const void* as2) {
134 return loc_as_cmp(*(struct loc_as**)as1, *(struct loc_as**)as2);
135}
136
137LOC_EXPORT int loc_writer_add_as(struct loc_writer* writer, struct loc_as** as, uint32_t number) {
29bd8211 138 int r = loc_as_new(writer->ctx, as, number);
c182393f
MT
139 if (r)
140 return r;
141
142 // We have a new AS to add
143 writer->as_count++;
144
145 // Make space
146 writer->as = realloc(writer->as, sizeof(*writer->as) * writer->as_count);
147 if (!writer->as)
148 return -ENOMEM;
149
150 // Add as last element
151 writer->as[writer->as_count - 1] = loc_as_ref(*as);
152
153 // Sort everything
154 qsort(writer->as, writer->as_count, sizeof(*writer->as), __loc_as_cmp);
155
156 return 0;
157}
158
3b5f4af2
MT
159LOC_EXPORT int loc_writer_add_network(struct loc_writer* writer, struct loc_network** network, const char* string) {
160 int r;
161
162 // Create a new network object
163 r = loc_network_new_from_string(writer->ctx, network, string);
164 if (r)
165 return r;
166
167 // Add it to the local tree
168 return loc_network_tree_add_network(writer->networks, *network);
169}
170
c182393f
MT
171static void make_magic(struct loc_writer* writer, struct loc_database_magic* magic) {
172 // Copy magic bytes
173 for (unsigned int i = 0; i < strlen(LOC_DATABASE_MAGIC); i++)
174 magic->magic[i] = LOC_DATABASE_MAGIC[i];
175
176 // Set version
0676cd80 177 magic->version = htobe16(LOC_DATABASE_VERSION);
c182393f
MT
178}
179
180static void align_page_boundary(off_t* offset, FILE* f) {
181 // Move to next page boundary
182 while (*offset % LOC_DATABASE_PAGE_SIZE > 0)
183 *offset += fwrite("", 1, 1, f);
184}
185
186static int loc_database_write_pool(struct loc_writer* writer,
187 struct loc_database_header_v0* header, off_t* offset, FILE* f) {
188 // Save the offset of the pool section
189 DEBUG(writer->ctx, "Pool starts at %jd bytes\n", *offset);
0676cd80 190 header->pool_offset = htobe32(*offset);
c182393f
MT
191
192 // Write the pool
193 size_t pool_length = loc_stringpool_write(writer->pool, f);
194 *offset += pool_length;
195
196 DEBUG(writer->ctx, "Pool has a length of %zu bytes\n", pool_length);
0676cd80 197 header->pool_length = htobe32(pool_length);
c182393f
MT
198
199 return 0;
200}
201
202static int loc_database_write_as_section(struct loc_writer* writer,
203 struct loc_database_header_v0* header, off_t* offset, FILE* f) {
204 DEBUG(writer->ctx, "AS section starts at %jd bytes\n", *offset);
0676cd80 205 header->as_offset = htobe32(*offset);
c182393f
MT
206
207 size_t as_length = 0;
208
209 struct loc_database_as_v0 as;
210 for (unsigned int i = 0; i < writer->as_count; i++) {
211 // Convert AS into database format
29bd8211 212 loc_as_to_database_v0(writer->as[i], writer->pool, &as);
c182393f
MT
213
214 // Write to disk
65862405 215 *offset += fwrite(&as, 1, sizeof(as), f);
c182393f
MT
216 as_length += sizeof(as);
217 }
218
219 DEBUG(writer->ctx, "AS section has a length of %zu bytes\n", as_length);
0676cd80 220 header->as_length = htobe32(as_length);
c182393f
MT
221
222 return 0;
223}
224
438db08c
MT
225struct node {
226 TAILQ_ENTRY(node) nodes;
f3e02bc5 227
438db08c 228 struct loc_network_tree_node* node;
f3e02bc5 229
438db08c
MT
230 // Indices of the child nodes
231 uint32_t index_zero;
232 uint32_t index_one;
233};
f3e02bc5 234
438db08c
MT
235static struct node* make_node(struct loc_network_tree_node* node) {
236 struct node* n = malloc(sizeof(*n));
237 if (!n)
238 return NULL;
f3e02bc5 239
438db08c
MT
240 n->node = loc_network_tree_node_ref(node);
241 n->index_zero = n->index_one = 0;
242
243 return n;
244}
245
246static void free_node(struct node* node) {
247 loc_network_tree_node_unref(node->node);
248
249 free(node);
250}
251
252struct network {
253 TAILQ_ENTRY(network) networks;
254
255 struct loc_network* network;
256};
257
258static struct network* make_network(struct loc_network* network) {
259 struct network* n = malloc(sizeof(*n));
260 if (!n)
261 return NULL;
262
263 n->network = loc_network_ref(network);
264
265 return n;
f3e02bc5
MT
266}
267
438db08c
MT
268static void free_network(struct network* network) {
269 loc_network_unref(network->network);
270
271 free(network);
272}
273
274static int loc_database_write_networks(struct loc_writer* writer,
f3e02bc5 275 struct loc_database_header_v0* header, off_t* offset, FILE* f) {
438db08c
MT
276 // Write the network tree
277 DEBUG(writer->ctx, "Network tree starts at %jd bytes\n", *offset);
278 header->network_tree_offset = htobe32(*offset);
f3e02bc5 279
438db08c
MT
280 size_t network_tree_length = 0;
281 size_t network_data_length = 0;
f3e02bc5 282
438db08c
MT
283 struct node* node;
284 struct node* child_node;
285
286 uint32_t index = 0;
287 uint32_t network_index = 0;
288
289 struct loc_database_network_v0 db_network;
290 struct loc_database_network_node_v0 db_node;
291
292 // Initialize queue for nodes
293 TAILQ_HEAD(node_t, node) nodes;
294 TAILQ_INIT(&nodes);
295
296 // Initialize queue for networks
297 TAILQ_HEAD(network_t, network) networks;
298 TAILQ_INIT(&networks);
299
300 // Add root
301 struct loc_network_tree_node* root = loc_network_tree_get_root(writer->networks);
302 node = make_node(root);
303
304 TAILQ_INSERT_TAIL(&nodes, node, nodes);
305
306 while (!TAILQ_EMPTY(&nodes)) {
307 // Pop first node in list
308 node = TAILQ_FIRST(&nodes);
309 TAILQ_REMOVE(&nodes, node, nodes);
310
311 DEBUG(writer->ctx, "Processing node %p\n", node);
312
313 // Get child nodes
314 struct loc_network_tree_node* node_zero = loc_network_tree_node_get(node->node, 0);
315 if (node_zero) {
316 node->index_zero = ++index;
317
318 child_node = make_node(node_zero);
319 loc_network_tree_node_unref(node_zero);
320
321 TAILQ_INSERT_TAIL(&nodes, child_node, nodes);
322 }
323
324 struct loc_network_tree_node* node_one = loc_network_tree_node_get(node->node, 1);
325 if (node_one) {
326 node->index_one = ++index;
327
328 child_node = make_node(node_one);
329 loc_network_tree_node_unref(node_one);
330
331 TAILQ_INSERT_TAIL(&nodes, child_node, nodes);
332 }
333
334 // Prepare what we are writing to disk
335 if (loc_network_tree_node_is_leaf(node->node)) {
336 struct loc_network* network = loc_network_tree_node_get_network(node->node);
337
338 // Append network to be written out later
339 struct network* nw = make_network(network);
340 TAILQ_INSERT_TAIL(&networks, nw, networks);
341
342 db_node.zero = htobe32(0xffffffff);
343 db_node.one = htobe32(network_index++);
344
345 loc_network_unref(network);
346 } else {
347 db_node.zero = htobe32(node->index_zero);
348 db_node.one = htobe32(node->index_one);
349 }
350
351 // Write the current node
352 DEBUG(writer->ctx, "Writing node %p (0 = %d, 1 = %d)\n",
353 node, node->index_zero, node->index_one);
354
355 *offset += fwrite(&db_node, 1, sizeof(db_node), f);
356 network_tree_length += sizeof(db_node);
357
358 free_node(node);
359 }
360
361 loc_network_tree_node_unref(root);
362
363 header->network_tree_length = htobe32(network_tree_length);
364
365 align_page_boundary(offset, f);
366
367 DEBUG(writer->ctx, "Networks data section starts at %jd bytes\n", *offset);
368 header->network_data_offset = htobe32(*offset);
369
370 // We have now written the entire tree and have all networks
371 // in a queue in order as they are indexed
372 while (!TAILQ_EMPTY(&networks)) {
373 struct network* nw = TAILQ_FIRST(&networks);
374 TAILQ_REMOVE(&networks, nw, networks);
375
376 // Prepare what we are writing to disk
377 int r = loc_network_to_database_v0(nw->network, &db_network);
378 if (r)
379 return r;
380
381 *offset += fwrite(&db_network, 1, sizeof(db_network), f);
382 network_data_length += sizeof(db_network);
383
384 free_network(nw);
385 }
f3e02bc5 386
438db08c 387 header->network_data_length = htobe32(network_data_length);
f3e02bc5
MT
388
389 return 0;
390}
391
c182393f
MT
392LOC_EXPORT int loc_writer_write(struct loc_writer* writer, FILE* f) {
393 struct loc_database_magic magic;
394 make_magic(writer, &magic);
395
396 // Make the header
397 struct loc_database_header_v0 header;
0676cd80
MT
398 header.vendor = htobe32(writer->vendor);
399 header.description = htobe32(writer->description);
c182393f
MT
400
401 time_t now = time(NULL);
402 header.created_at = htobe64(now);
403
404 int r;
405 off_t offset = 0;
406
407 // Start writing at the beginning of the file
408 r = fseek(f, 0, SEEK_SET);
409 if (r)
410 return r;
411
412 // Write the magic
413 offset += fwrite(&magic, 1, sizeof(magic), f);
414
415 // Skip the space we need to write the header later
416 r = fseek(f, sizeof(header), SEEK_CUR);
417 if (r) {
418 DEBUG(writer->ctx, "Could not seek to position after header\n");
419 return r;
420 }
421 offset += sizeof(header);
422
423 align_page_boundary(&offset, f);
424
425 // Write all ASes
426 r = loc_database_write_as_section(writer, &header, &offset, f);
427 if (r)
428 return r;
429
430 align_page_boundary(&offset, f);
431
f3e02bc5 432 // Write all networks
438db08c 433 r = loc_database_write_networks(writer, &header, &offset, f);
f3e02bc5
MT
434 if (r)
435 return r;
436
437 align_page_boundary(&offset, f);
438
c182393f
MT
439 // Write pool
440 r = loc_database_write_pool(writer, &header, &offset, f);
441 if (r)
442 return r;
443
444 // Write the header
445 r = fseek(f, sizeof(magic), SEEK_SET);
446 if (r)
447 return r;
448
449 offset += fwrite(&header, 1, sizeof(header), f);
450
451 return 0;
452}