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