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