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.
22 #include <sys/queue.h>
25 #include <loc/libloc.h>
27 #include <loc/format.h>
28 #include <loc/network.h>
29 #include <loc/private.h>
30 #include <loc/writer.h>
36 struct loc_stringpool
* pool
;
44 struct loc_network_tree
* networks
;
47 LOC_EXPORT
int loc_writer_new(struct loc_ctx
* ctx
, struct loc_writer
** writer
) {
48 struct loc_writer
* w
= calloc(1, sizeof(*w
));
52 w
->ctx
= loc_ref(ctx
);
55 int r
= loc_stringpool_new(ctx
, &w
->pool
);
61 // Initialize the network tree
62 r
= loc_network_tree_new(ctx
, &w
->networks
);
72 LOC_EXPORT
struct loc_writer
* loc_writer_ref(struct loc_writer
* writer
) {
78 static void loc_writer_free(struct loc_writer
* writer
) {
79 DEBUG(writer
->ctx
, "Releasing writer at %p\n", writer
);
82 for (unsigned int i
= 0; i
< writer
->as_count
; i
++) {
83 loc_as_unref(writer
->as
[i
]);
86 // Release network tree
88 loc_network_tree_unref(writer
->networks
);
90 // Unref the string pool
91 loc_stringpool_unref(writer
->pool
);
93 loc_unref(writer
->ctx
);
97 LOC_EXPORT
struct loc_writer
* loc_writer_unref(struct loc_writer
* writer
) {
98 if (--writer
->refcount
> 0)
101 loc_writer_free(writer
);
106 LOC_EXPORT
const char* loc_writer_get_vendor(struct loc_writer
* writer
) {
107 return loc_stringpool_get(writer
->pool
, writer
->vendor
);
110 LOC_EXPORT
int loc_writer_set_vendor(struct loc_writer
* writer
, const char* vendor
) {
111 // Add the string to the string pool
112 off_t offset
= loc_stringpool_add(writer
->pool
, vendor
);
116 writer
->vendor
= offset
;
120 LOC_EXPORT
const char* loc_writer_get_description(struct loc_writer
* writer
) {
121 return loc_stringpool_get(writer
->pool
, writer
->description
);
124 LOC_EXPORT
int loc_writer_set_description(struct loc_writer
* writer
, const char* description
) {
125 // Add the string to the string pool
126 off_t offset
= loc_stringpool_add(writer
->pool
, description
);
130 writer
->description
= offset
;
134 LOC_EXPORT
const char* loc_writer_get_license(struct loc_writer
* writer
) {
135 return loc_stringpool_get(writer
->pool
, writer
->license
);
138 LOC_EXPORT
int loc_writer_set_license(struct loc_writer
* writer
, const char* license
) {
139 // Add the string to the string pool
140 off_t offset
= loc_stringpool_add(writer
->pool
, license
);
144 writer
->license
= offset
;
148 static int __loc_as_cmp(const void* as1
, const void* as2
) {
149 return loc_as_cmp(*(struct loc_as
**)as1
, *(struct loc_as
**)as2
);
152 LOC_EXPORT
int loc_writer_add_as(struct loc_writer
* writer
, struct loc_as
** as
, uint32_t number
) {
153 int r
= loc_as_new(writer
->ctx
, as
, number
);
157 // We have a new AS to add
161 writer
->as
= realloc(writer
->as
, sizeof(*writer
->as
) * writer
->as_count
);
165 // Add as last element
166 writer
->as
[writer
->as_count
- 1] = loc_as_ref(*as
);
169 qsort(writer
->as
, writer
->as_count
, sizeof(*writer
->as
), __loc_as_cmp
);
174 LOC_EXPORT
int loc_writer_add_network(struct loc_writer
* writer
, struct loc_network
** network
, const char* string
) {
177 // Create a new network object
178 r
= loc_network_new_from_string(writer
->ctx
, network
, string
);
182 // Add it to the local tree
183 return loc_network_tree_add_network(writer
->networks
, *network
);
186 static void make_magic(struct loc_writer
* writer
, struct loc_database_magic
* magic
) {
188 for (unsigned int i
= 0; i
< strlen(LOC_DATABASE_MAGIC
); i
++)
189 magic
->magic
[i
] = LOC_DATABASE_MAGIC
[i
];
192 magic
->version
= htobe16(LOC_DATABASE_VERSION
);
195 static void align_page_boundary(off_t
* offset
, FILE* f
) {
196 // Move to next page boundary
197 while (*offset
% LOC_DATABASE_PAGE_SIZE
> 0)
198 *offset
+= fwrite("", 1, 1, f
);
201 static int loc_database_write_pool(struct loc_writer
* writer
,
202 struct loc_database_header_v0
* header
, off_t
* offset
, FILE* f
) {
203 // Save the offset of the pool section
204 DEBUG(writer
->ctx
, "Pool starts at %jd bytes\n", *offset
);
205 header
->pool_offset
= htobe32(*offset
);
208 size_t pool_length
= loc_stringpool_write(writer
->pool
, f
);
209 *offset
+= pool_length
;
211 DEBUG(writer
->ctx
, "Pool has a length of %zu bytes\n", pool_length
);
212 header
->pool_length
= htobe32(pool_length
);
217 static int loc_database_write_as_section(struct loc_writer
* writer
,
218 struct loc_database_header_v0
* header
, off_t
* offset
, FILE* f
) {
219 DEBUG(writer
->ctx
, "AS section starts at %jd bytes\n", *offset
);
220 header
->as_offset
= htobe32(*offset
);
222 size_t as_length
= 0;
224 struct loc_database_as_v0 as
;
225 for (unsigned int i
= 0; i
< writer
->as_count
; i
++) {
226 // Convert AS into database format
227 loc_as_to_database_v0(writer
->as
[i
], writer
->pool
, &as
);
230 *offset
+= fwrite(&as
, 1, sizeof(as
), f
);
231 as_length
+= sizeof(as
);
234 DEBUG(writer
->ctx
, "AS section has a length of %zu bytes\n", as_length
);
235 header
->as_length
= htobe32(as_length
);
237 align_page_boundary(offset
, f
);
243 TAILQ_ENTRY(node
) nodes
;
245 struct loc_network_tree_node
* node
;
247 // Indices of the child nodes
252 static struct node
* make_node(struct loc_network_tree_node
* node
) {
253 struct node
* n
= malloc(sizeof(*n
));
257 n
->node
= loc_network_tree_node_ref(node
);
258 n
->index_zero
= n
->index_one
= 0;
263 static void free_node(struct node
* node
) {
264 loc_network_tree_node_unref(node
->node
);
270 TAILQ_ENTRY(network
) networks
;
272 struct loc_network
* network
;
275 static struct network
* make_network(struct loc_network
* network
) {
276 struct network
* n
= malloc(sizeof(*n
));
280 n
->network
= loc_network_ref(network
);
285 static void free_network(struct network
* network
) {
286 loc_network_unref(network
->network
);
291 static int loc_database_write_networks(struct loc_writer
* writer
,
292 struct loc_database_header_v0
* header
, off_t
* offset
, FILE* f
) {
293 // Write the network tree
294 DEBUG(writer
->ctx
, "Network tree starts at %jd bytes\n", *offset
);
295 header
->network_tree_offset
= htobe32(*offset
);
297 size_t network_tree_length
= 0;
298 size_t network_data_length
= 0;
301 struct node
* child_node
;
304 uint32_t network_index
= 0;
306 struct loc_database_network_v0 db_network
;
307 struct loc_database_network_node_v0 db_node
;
309 // Initialize queue for nodes
310 TAILQ_HEAD(node_t
, node
) nodes
;
313 // Initialize queue for networks
314 TAILQ_HEAD(network_t
, network
) networks
;
315 TAILQ_INIT(&networks
);
318 struct loc_network_tree_node
* root
= loc_network_tree_get_root(writer
->networks
);
319 node
= make_node(root
);
321 TAILQ_INSERT_TAIL(&nodes
, node
, nodes
);
323 while (!TAILQ_EMPTY(&nodes
)) {
324 // Pop first node in list
325 node
= TAILQ_FIRST(&nodes
);
326 TAILQ_REMOVE(&nodes
, node
, nodes
);
328 DEBUG(writer
->ctx
, "Processing node %p\n", node
);
331 struct loc_network_tree_node
* node_zero
= loc_network_tree_node_get(node
->node
, 0);
333 node
->index_zero
= ++index
;
335 child_node
= make_node(node_zero
);
336 loc_network_tree_node_unref(node_zero
);
338 TAILQ_INSERT_TAIL(&nodes
, child_node
, nodes
);
341 struct loc_network_tree_node
* node_one
= loc_network_tree_node_get(node
->node
, 1);
343 node
->index_one
= ++index
;
345 child_node
= make_node(node_one
);
346 loc_network_tree_node_unref(node_one
);
348 TAILQ_INSERT_TAIL(&nodes
, child_node
, nodes
);
351 // Prepare what we are writing to disk
352 db_node
.zero
= htobe32(node
->index_zero
);
353 db_node
.one
= htobe32(node
->index_one
);
355 if (loc_network_tree_node_is_leaf(node
->node
)) {
356 struct loc_network
* network
= loc_network_tree_node_get_network(node
->node
);
358 // Append network to be written out later
359 struct network
* nw
= make_network(network
);
360 TAILQ_INSERT_TAIL(&networks
, nw
, networks
);
362 db_node
.network
= htobe32(network_index
++);
363 loc_network_unref(network
);
365 db_node
.network
= htobe32(0xffffffff);
368 // Write the current node
369 DEBUG(writer
->ctx
, "Writing node %p (0 = %d, 1 = %d)\n",
370 node
, node
->index_zero
, node
->index_one
);
372 *offset
+= fwrite(&db_node
, 1, sizeof(db_node
), f
);
373 network_tree_length
+= sizeof(db_node
);
378 loc_network_tree_node_unref(root
);
380 header
->network_tree_length
= htobe32(network_tree_length
);
382 align_page_boundary(offset
, f
);
384 DEBUG(writer
->ctx
, "Networks data section starts at %jd bytes\n", *offset
);
385 header
->network_data_offset
= htobe32(*offset
);
387 // We have now written the entire tree and have all networks
388 // in a queue in order as they are indexed
389 while (!TAILQ_EMPTY(&networks
)) {
390 struct network
* nw
= TAILQ_FIRST(&networks
);
391 TAILQ_REMOVE(&networks
, nw
, networks
);
393 // Prepare what we are writing to disk
394 int r
= loc_network_to_database_v0(nw
->network
, &db_network
);
398 *offset
+= fwrite(&db_network
, 1, sizeof(db_network
), f
);
399 network_data_length
+= sizeof(db_network
);
404 header
->network_data_length
= htobe32(network_data_length
);
406 align_page_boundary(offset
, f
);
411 LOC_EXPORT
int loc_writer_write(struct loc_writer
* writer
, FILE* f
) {
412 struct loc_database_magic magic
;
413 make_magic(writer
, &magic
);
416 struct loc_database_header_v0 header
;
417 header
.vendor
= htobe32(writer
->vendor
);
418 header
.description
= htobe32(writer
->description
);
419 header
.license
= htobe32(writer
->license
);
421 time_t now
= time(NULL
);
422 header
.created_at
= htobe64(now
);
427 // Start writing at the beginning of the file
428 r
= fseek(f
, 0, SEEK_SET
);
433 offset
+= fwrite(&magic
, 1, sizeof(magic
), f
);
435 // Skip the space we need to write the header later
436 r
= fseek(f
, sizeof(header
), SEEK_CUR
);
438 DEBUG(writer
->ctx
, "Could not seek to position after header\n");
441 offset
+= sizeof(header
);
443 align_page_boundary(&offset
, f
);
446 r
= loc_database_write_as_section(writer
, &header
, &offset
, f
);
450 // Write all networks
451 r
= loc_database_write_networks(writer
, &header
, &offset
, f
);
456 r
= loc_database_write_pool(writer
, &header
, &offset
, f
);
461 r
= fseek(f
, sizeof(magic
), SEEK_SET
);
465 offset
+= fwrite(&header
, 1, sizeof(header
), f
);