]> git.ipfire.org Git - people/ms/libloc.git/blame - src/writer.c
Makefile: Don't try to use -export-symbols if --version-script= isn't available
[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
726f9984 17#include <assert.h>
c182393f
MT
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
42f3ccd7
MT
25#ifdef HAVE_ENDIAN_H
26# include <endian.h>
27#endif
28
726f9984
MT
29#include <openssl/bio.h>
30#include <openssl/err.h>
31#include <openssl/evp.h>
32#include <openssl/pem.h>
33
c12a1385
MT
34#include <libloc/libloc.h>
35#include <libloc/as.h>
233311e8 36#include <libloc/as-list.h>
c12a1385
MT
37#include <libloc/compat.h>
38#include <libloc/country.h>
3df5f70a 39#include <libloc/country-list.h>
c12a1385
MT
40#include <libloc/database.h>
41#include <libloc/format.h>
42#include <libloc/network.h>
fe8277ed 43#include <libloc/network-tree.h>
c12a1385
MT
44#include <libloc/private.h>
45#include <libloc/writer.h>
c182393f 46
c182393f
MT
47struct loc_writer {
48 struct loc_ctx* ctx;
49 int refcount;
50
51 struct loc_stringpool* pool;
52 off_t vendor;
53 off_t description;
4bf49d00 54 off_t license;
c182393f 55
5ce881d4
MT
56 // Private keys to sign any databases
57 EVP_PKEY* private_key1;
58 EVP_PKEY* private_key2;
59
60 // Signatures
61 char signature1[LOC_SIGNATURE_MAX_LENGTH];
62 size_t signature1_length;
63 char signature2[LOC_SIGNATURE_MAX_LENGTH];
64 size_t signature2_length;
726f9984 65
3b5f4af2 66 struct loc_network_tree* networks;
233311e8
MT
67
68 struct loc_as_list* as_list;
3df5f70a 69 struct loc_country_list* country_list;
c182393f
MT
70};
71
5ce881d4 72static int parse_private_key(struct loc_writer* writer, EVP_PKEY** private_key, FILE* f) {
726f9984 73 // Free any previously loaded keys
5ce881d4
MT
74 if (*private_key)
75 EVP_PKEY_free(*private_key);
726f9984
MT
76
77 // Read the key
5ce881d4 78 *private_key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
726f9984
MT
79
80 // Log any errors
5ce881d4 81 if (!*private_key) {
726f9984
MT
82 char* error = ERR_error_string(ERR_get_error(), NULL);
83 ERROR(writer->ctx, "Could not parse private key: %s\n", error);
84
85 return -1;
86 }
87
88 return 0;
89}
90
5ce881d4
MT
91LOC_EXPORT int loc_writer_new(struct loc_ctx* ctx, struct loc_writer** writer,
92 FILE* fkey1, FILE* fkey2) {
c182393f
MT
93 struct loc_writer* w = calloc(1, sizeof(*w));
94 if (!w)
198e382c 95 return 1;
c182393f
MT
96
97 w->ctx = loc_ref(ctx);
98 w->refcount = 1;
99
0e974d4b 100 int r = loc_stringpool_new(ctx, &w->pool);
c182393f
MT
101 if (r) {
102 loc_writer_unref(w);
103 return r;
104 }
105
c5a722cc
MT
106 // Add an empty string to the stringpool
107 r = loc_stringpool_add(w->pool, "");
108 if (r) {
109 loc_writer_unref(w);
110 return r;
111 }
112
3b5f4af2
MT
113 // Initialize the network tree
114 r = loc_network_tree_new(ctx, &w->networks);
115 if (r) {
116 loc_writer_unref(w);
117 return r;
118 }
119
233311e8
MT
120 // Initialize AS list
121 r = loc_as_list_new(ctx, &w->as_list);
122 if (r) {
123 loc_writer_unref(w);
124 return r;
125 }
126
d57cf1fc
MT
127 // Initialize countries list
128 r = loc_country_list_new(ctx, &w->country_list);
129 if (r) {
130 loc_writer_unref(w);
131 return r;
132 }
133
5ce881d4
MT
134 // Load the private keys to sign databases
135 if (fkey1) {
136 r = parse_private_key(w, &w->private_key1, fkey1);
137 if (r) {
138 loc_writer_unref(w);
139 return r;
140 }
141 }
142
143 if (fkey2) {
144 r = parse_private_key(w, &w->private_key2, fkey2);
726f9984
MT
145 if (r) {
146 loc_writer_unref(w);
147 return r;
148 }
149 }
150
c182393f
MT
151 *writer = w;
152 return 0;
153}
154
155LOC_EXPORT struct loc_writer* loc_writer_ref(struct loc_writer* writer) {
156 writer->refcount++;
157
158 return writer;
159}
160
161static void loc_writer_free(struct loc_writer* writer) {
162 DEBUG(writer->ctx, "Releasing writer at %p\n", writer);
163
5ce881d4
MT
164 // Free private keys
165 if (writer->private_key1)
166 EVP_PKEY_free(writer->private_key1);
167 if (writer->private_key2)
168 EVP_PKEY_free(writer->private_key2);
726f9984 169
c182393f 170 // Unref all AS
233311e8
MT
171 if (writer->as_list)
172 loc_as_list_unref(writer->as_list);
c182393f 173
d89a7d62 174 // Unref all countries
3df5f70a
MT
175 if (writer->country_list)
176 loc_country_list_unref(writer->country_list);
d89a7d62 177
3b5f4af2
MT
178 // Release network tree
179 if (writer->networks)
180 loc_network_tree_unref(writer->networks);
181
c182393f 182 // Unref the string pool
156f2ea5
MT
183 if (writer->pool)
184 loc_stringpool_unref(writer->pool);
c182393f
MT
185
186 loc_unref(writer->ctx);
187 free(writer);
188}
189
190LOC_EXPORT struct loc_writer* loc_writer_unref(struct loc_writer* writer) {
191 if (--writer->refcount > 0)
192 return writer;
193
194 loc_writer_free(writer);
195
196 return NULL;
197}
198
199LOC_EXPORT const char* loc_writer_get_vendor(struct loc_writer* writer) {
200 return loc_stringpool_get(writer->pool, writer->vendor);
201}
202
203LOC_EXPORT int loc_writer_set_vendor(struct loc_writer* writer, const char* vendor) {
204 // Add the string to the string pool
205 off_t offset = loc_stringpool_add(writer->pool, vendor);
206 if (offset < 0)
207 return offset;
208
209 writer->vendor = offset;
210 return 0;
211}
212
213LOC_EXPORT const char* loc_writer_get_description(struct loc_writer* writer) {
214 return loc_stringpool_get(writer->pool, writer->description);
215}
216
217LOC_EXPORT int loc_writer_set_description(struct loc_writer* writer, const char* description) {
218 // Add the string to the string pool
219 off_t offset = loc_stringpool_add(writer->pool, description);
220 if (offset < 0)
221 return offset;
222
223 writer->description = offset;
224 return 0;
225}
226
4bf49d00
MT
227LOC_EXPORT const char* loc_writer_get_license(struct loc_writer* writer) {
228 return loc_stringpool_get(writer->pool, writer->license);
229}
230
231LOC_EXPORT int loc_writer_set_license(struct loc_writer* writer, const char* license) {
232 // Add the string to the string pool
233 off_t offset = loc_stringpool_add(writer->pool, license);
234 if (offset < 0)
235 return offset;
236
237 writer->license = offset;
238 return 0;
239}
240
c182393f 241LOC_EXPORT int loc_writer_add_as(struct loc_writer* writer, struct loc_as** as, uint32_t number) {
233311e8 242 // Create a new AS object
29bd8211 243 int r = loc_as_new(writer->ctx, as, number);
c182393f
MT
244 if (r)
245 return r;
246
233311e8
MT
247 // Append it to the list
248 return loc_as_list_append(writer->as_list, *as);
c182393f
MT
249}
250
3b5f4af2
MT
251LOC_EXPORT int loc_writer_add_network(struct loc_writer* writer, struct loc_network** network, const char* string) {
252 int r;
253
254 // Create a new network object
255 r = loc_network_new_from_string(writer->ctx, network, string);
256 if (r)
257 return r;
258
259 // Add it to the local tree
260 return loc_network_tree_add_network(writer->networks, *network);
261}
262
ec684c1a 263LOC_EXPORT int loc_writer_add_country(struct loc_writer* writer, struct loc_country** country, const char* country_code) {
3df5f70a 264 // Allocate a new country
ec684c1a
MT
265 int r = loc_country_new(writer->ctx, country, country_code);
266 if (r)
267 return r;
268
3df5f70a
MT
269 // Append it to the list
270 return loc_country_list_append(writer->country_list, *country);
ec684c1a
MT
271}
272
22c7b98b
MT
273static void make_magic(struct loc_writer* writer, struct loc_database_magic* magic,
274 enum loc_database_version version) {
c182393f
MT
275 // Copy magic bytes
276 for (unsigned int i = 0; i < strlen(LOC_DATABASE_MAGIC); i++)
277 magic->magic[i] = LOC_DATABASE_MAGIC[i];
278
279 // Set version
22c7b98b 280 magic->version = version;
c182393f
MT
281}
282
283static void align_page_boundary(off_t* offset, FILE* f) {
284 // Move to next page boundary
285 while (*offset % LOC_DATABASE_PAGE_SIZE > 0)
286 *offset += fwrite("", 1, 1, f);
287}
288
289static int loc_database_write_pool(struct loc_writer* writer,
b904896a 290 struct loc_database_header_v1* header, off_t* offset, FILE* f) {
c182393f 291 // Save the offset of the pool section
5c57de03 292 DEBUG(writer->ctx, "Pool starts at %jd bytes\n", (intmax_t)*offset);
0676cd80 293 header->pool_offset = htobe32(*offset);
c182393f
MT
294
295 // Write the pool
296 size_t pool_length = loc_stringpool_write(writer->pool, f);
297 *offset += pool_length;
298
299 DEBUG(writer->ctx, "Pool has a length of %zu bytes\n", pool_length);
0676cd80 300 header->pool_length = htobe32(pool_length);
c182393f
MT
301
302 return 0;
303}
304
305static int loc_database_write_as_section(struct loc_writer* writer,
b904896a 306 struct loc_database_header_v1* header, off_t* offset, FILE* f) {
5c57de03 307 DEBUG(writer->ctx, "AS section starts at %jd bytes\n", (intmax_t)*offset);
0676cd80 308 header->as_offset = htobe32(*offset);
c182393f 309
233311e8
MT
310 // Sort the AS list first
311 loc_as_list_sort(writer->as_list);
312
313 const size_t as_count = loc_as_list_size(writer->as_list);
314
315 struct loc_database_as_v1 block;
316 size_t block_length = 0;
317
318 for (unsigned int i = 0; i < as_count; i++) {
319 struct loc_as* as = loc_as_list_get(writer->as_list, i);
320 if (!as)
321 return 1;
c182393f 322
c182393f 323 // Convert AS into database format
233311e8 324 loc_as_to_database_v1(as, writer->pool, &block);
c182393f
MT
325
326 // Write to disk
233311e8
MT
327 *offset += fwrite(&block, 1, sizeof(block), f);
328 block_length += sizeof(block);
329
330 // Unref AS
331 loc_as_unref(as);
c182393f
MT
332 }
333
233311e8
MT
334 DEBUG(writer->ctx, "AS section has a length of %zu bytes\n", block_length);
335 header->as_length = htobe32(block_length);
c182393f 336
fda89f24
MT
337 align_page_boundary(offset, f);
338
c182393f
MT
339 return 0;
340}
341
438db08c
MT
342struct node {
343 TAILQ_ENTRY(node) nodes;
f3e02bc5 344
438db08c 345 struct loc_network_tree_node* node;
f3e02bc5 346
438db08c
MT
347 // Indices of the child nodes
348 uint32_t index_zero;
349 uint32_t index_one;
350};
f3e02bc5 351
438db08c
MT
352static struct node* make_node(struct loc_network_tree_node* node) {
353 struct node* n = malloc(sizeof(*n));
354 if (!n)
355 return NULL;
f3e02bc5 356
438db08c
MT
357 n->node = loc_network_tree_node_ref(node);
358 n->index_zero = n->index_one = 0;
359
360 return n;
361}
362
363static void free_node(struct node* node) {
364 loc_network_tree_node_unref(node->node);
365
366 free(node);
367}
368
369struct network {
370 TAILQ_ENTRY(network) networks;
371
372 struct loc_network* network;
373};
374
375static struct network* make_network(struct loc_network* network) {
376 struct network* n = malloc(sizeof(*n));
377 if (!n)
378 return NULL;
379
380 n->network = loc_network_ref(network);
381
382 return n;
f3e02bc5
MT
383}
384
438db08c
MT
385static void free_network(struct network* network) {
386 loc_network_unref(network->network);
387
388 free(network);
389}
390
391static int loc_database_write_networks(struct loc_writer* writer,
b904896a 392 struct loc_database_header_v1* header, off_t* offset, FILE* f) {
f6c6b8d6
MT
393 int r;
394
438db08c 395 // Write the network tree
5c57de03 396 DEBUG(writer->ctx, "Network tree starts at %jd bytes\n", (intmax_t)*offset);
438db08c 397 header->network_tree_offset = htobe32(*offset);
f3e02bc5 398
438db08c
MT
399 size_t network_tree_length = 0;
400 size_t network_data_length = 0;
f3e02bc5 401
438db08c
MT
402 struct node* node;
403 struct node* child_node;
404
405 uint32_t index = 0;
406 uint32_t network_index = 0;
407
b904896a
MT
408 struct loc_database_network_v1 db_network;
409 struct loc_database_network_node_v1 db_node;
438db08c
MT
410
411 // Initialize queue for nodes
412 TAILQ_HEAD(node_t, node) nodes;
413 TAILQ_INIT(&nodes);
414
415 // Initialize queue for networks
416 TAILQ_HEAD(network_t, network) networks;
417 TAILQ_INIT(&networks);
418
f6c6b8d6
MT
419 // Cleanup the tree before writing it
420 r = loc_network_tree_cleanup(writer->networks);
421 if (r)
422 return r;
423
438db08c
MT
424 // Add root
425 struct loc_network_tree_node* root = loc_network_tree_get_root(writer->networks);
426 node = make_node(root);
d66bfbaa
MT
427 if (!node)
428 return 1;
438db08c
MT
429
430 TAILQ_INSERT_TAIL(&nodes, node, nodes);
431
432 while (!TAILQ_EMPTY(&nodes)) {
433 // Pop first node in list
434 node = TAILQ_FIRST(&nodes);
435 TAILQ_REMOVE(&nodes, node, nodes);
436
437 DEBUG(writer->ctx, "Processing node %p\n", node);
438
439 // Get child nodes
440 struct loc_network_tree_node* node_zero = loc_network_tree_node_get(node->node, 0);
441 if (node_zero) {
442 node->index_zero = ++index;
443
444 child_node = make_node(node_zero);
445 loc_network_tree_node_unref(node_zero);
446
447 TAILQ_INSERT_TAIL(&nodes, child_node, nodes);
448 }
449
450 struct loc_network_tree_node* node_one = loc_network_tree_node_get(node->node, 1);
451 if (node_one) {
452 node->index_one = ++index;
453
454 child_node = make_node(node_one);
455 loc_network_tree_node_unref(node_one);
456
457 TAILQ_INSERT_TAIL(&nodes, child_node, nodes);
458 }
459
460 // Prepare what we are writing to disk
39a55353
MT
461 db_node.zero = htobe32(node->index_zero);
462 db_node.one = htobe32(node->index_one);
463
438db08c
MT
464 if (loc_network_tree_node_is_leaf(node->node)) {
465 struct loc_network* network = loc_network_tree_node_get_network(node->node);
466
467 // Append network to be written out later
468 struct network* nw = make_network(network);
d66bfbaa
MT
469 if (!nw) {
470 free_node(node);
471 return 1;
472 }
438db08c
MT
473 TAILQ_INSERT_TAIL(&networks, nw, networks);
474
39a55353 475 db_node.network = htobe32(network_index++);
438db08c
MT
476 loc_network_unref(network);
477 } else {
39a55353 478 db_node.network = htobe32(0xffffffff);
438db08c
MT
479 }
480
481 // Write the current node
482 DEBUG(writer->ctx, "Writing node %p (0 = %d, 1 = %d)\n",
483 node, node->index_zero, node->index_one);
484
485 *offset += fwrite(&db_node, 1, sizeof(db_node), f);
486 network_tree_length += sizeof(db_node);
487
488 free_node(node);
489 }
490
491 loc_network_tree_node_unref(root);
492
493 header->network_tree_length = htobe32(network_tree_length);
494
495 align_page_boundary(offset, f);
496
5c57de03 497 DEBUG(writer->ctx, "Networks data section starts at %jd bytes\n", (intmax_t)*offset);
438db08c
MT
498 header->network_data_offset = htobe32(*offset);
499
500 // We have now written the entire tree and have all networks
501 // in a queue in order as they are indexed
502 while (!TAILQ_EMPTY(&networks)) {
503 struct network* nw = TAILQ_FIRST(&networks);
504 TAILQ_REMOVE(&networks, nw, networks);
505
506 // Prepare what we are writing to disk
f6c6b8d6 507 r = loc_network_to_database_v1(nw->network, &db_network);
438db08c
MT
508 if (r)
509 return r;
510
511 *offset += fwrite(&db_network, 1, sizeof(db_network), f);
512 network_data_length += sizeof(db_network);
513
514 free_network(nw);
515 }
f3e02bc5 516
438db08c 517 header->network_data_length = htobe32(network_data_length);
f3e02bc5 518
fda89f24
MT
519 align_page_boundary(offset, f);
520
f3e02bc5
MT
521 return 0;
522}
523
ec684c1a 524static int loc_database_write_countries(struct loc_writer* writer,
b904896a 525 struct loc_database_header_v1* header, off_t* offset, FILE* f) {
2e2325a9 526 DEBUG(writer->ctx, "Countries section starts at %jd bytes\n", (intmax_t)*offset);
ec684c1a
MT
527 header->countries_offset = htobe32(*offset);
528
3df5f70a
MT
529 const size_t countries_count = loc_country_list_size(writer->country_list);
530
531 struct loc_database_country_v1 block;
532 size_t block_length = 0;
533
534 for (unsigned int i = 0; i < countries_count; i++) {
535 struct loc_country* country = loc_country_list_get(writer->country_list, i);
ec684c1a 536
ec684c1a 537 // Convert country into database format
3df5f70a 538 loc_country_to_database_v1(country, writer->pool, &block);
ec684c1a
MT
539
540 // Write to disk
3df5f70a
MT
541 *offset += fwrite(&block, 1, sizeof(block), f);
542 block_length += sizeof(block);
ec684c1a
MT
543 }
544
3df5f70a
MT
545 DEBUG(writer->ctx, "Countries section has a length of %zu bytes\n", block_length);
546 header->countries_length = htobe32(block_length);
ec684c1a
MT
547
548 align_page_boundary(offset, f);
549
550 return 0;
551}
552
726f9984 553static int loc_writer_create_signature(struct loc_writer* writer,
5ce881d4
MT
554 struct loc_database_header_v1* header, FILE* f, EVP_PKEY* private_key,
555 char* signature, size_t* length) {
98b2876e
MT
556 size_t bytes_read = 0;
557
c7db968a 558 DEBUG(writer->ctx, "Creating signature...\n");
726f9984
MT
559
560 // Read file from the beginning
561 rewind(f);
562
563 // Create a new context for signing
564 EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
565
566 // Initialise the context
5ce881d4 567 int r = EVP_DigestSignInit(mdctx, NULL, NULL, NULL, private_key);
726f9984
MT
568 if (r != 1) {
569 ERROR(writer->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
570 goto END;
571 }
572
573 // Read magic
574 struct loc_database_magic magic;
98b2876e
MT
575 bytes_read = fread(&magic, 1, sizeof(magic), f);
576 if (bytes_read < sizeof(magic)) {
577 ERROR(writer->ctx, "Could not read header: %m\n");
578 r = 1;
579 goto END;
580 }
726f9984 581
a0cff45d
MT
582 hexdump(writer->ctx, &magic, sizeof(magic));
583
726f9984
MT
584 // Feed magic into the signature
585 r = EVP_DigestSignUpdate(mdctx, &magic, sizeof(magic));
586 if (r != 1) {
587 ERROR(writer->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
588 goto END;
589 }
590
a0cff45d
MT
591 hexdump(writer->ctx, header, sizeof(*header));
592
726f9984 593 // Feed the header into the signature
72633a85 594 r = EVP_DigestSignUpdate(mdctx, header, sizeof(*header));
726f9984
MT
595 if (r != 1) {
596 ERROR(writer->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
597 goto END;
598 }
599
600 // Skip header
72633a85 601 fseek(f, sizeof(*header), SEEK_CUR);
726f9984
MT
602
603 // Walk through the file in chunks of 64kB
604 char buffer[64 * 1024];
605 while (!feof(f)) {
98b2876e 606 bytes_read = fread(buffer, 1, sizeof(buffer), f);
726f9984
MT
607
608 if (ferror(f)) {
6cd02f1d 609 ERROR(writer->ctx, "Error reading from file: %m\n");
198e382c 610 r = 1;
726f9984
MT
611 goto END;
612 }
613
a0cff45d
MT
614 hexdump(writer->ctx, buffer, bytes_read);
615
726f9984
MT
616 r = EVP_DigestSignUpdate(mdctx, buffer, bytes_read);
617 if (r != 1) {
618 ERROR(writer->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
5ce881d4 619 r = -1;
726f9984
MT
620 goto END;
621 }
622 }
623
624 // Compute the signature
726f9984 625 r = EVP_DigestSignFinal(mdctx,
5ce881d4 626 (unsigned char*)signature, length);
726f9984
MT
627 if (r != 1) {
628 ERROR(writer->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
5ce881d4 629 r = -1;
726f9984
MT
630 goto END;
631 }
632
36b9fd75 633 DEBUG(writer->ctx, "Successfully generated signature of %zu bytes\n", *length);
726f9984
MT
634 r = 0;
635
257626f5 636 // Dump signature
5ce881d4 637 hexdump(writer->ctx, signature, *length);
257626f5 638
726f9984
MT
639END:
640 EVP_MD_CTX_free(mdctx);
641
642 return r;
643}
644
22c7b98b
MT
645LOC_EXPORT int loc_writer_write(struct loc_writer* writer, FILE* f, enum loc_database_version version) {
646 // Check version
647 switch (version) {
648 case LOC_DATABASE_VERSION_UNSET:
649 version = LOC_DATABASE_VERSION_LATEST;
650 break;
651
652 case LOC_DATABASE_VERSION_1:
653 break;
654
655 default:
656 ERROR(writer->ctx, "Invalid database version: %d\n", version);
657 return -1;
658 }
659
660 DEBUG(writer->ctx, "Writing database in version %d\n", version);
661
c182393f 662 struct loc_database_magic magic;
22c7b98b 663 make_magic(writer, &magic, version);
c182393f
MT
664
665 // Make the header
b904896a 666 struct loc_database_header_v1 header;
0676cd80
MT
667 header.vendor = htobe32(writer->vendor);
668 header.description = htobe32(writer->description);
4bf49d00 669 header.license = htobe32(writer->license);
c182393f
MT
670
671 time_t now = time(NULL);
672 header.created_at = htobe64(now);
673
5ce881d4
MT
674 // Clear the signatures
675 memset(header.signature1, '\0', sizeof(header.signature1));
676 header.signature1_length = 0;
677 memset(header.signature2, '\0', sizeof(header.signature2));
678 header.signature2_length = 0;
b1720435 679
5e164830 680 // Clear the padding
c7db968a 681 memset(header.padding, '\0', sizeof(header.padding));
5e164830 682
c182393f
MT
683 int r;
684 off_t offset = 0;
685
686 // Start writing at the beginning of the file
687 r = fseek(f, 0, SEEK_SET);
688 if (r)
689 return r;
690
691 // Write the magic
692 offset += fwrite(&magic, 1, sizeof(magic), f);
693
694 // Skip the space we need to write the header later
695 r = fseek(f, sizeof(header), SEEK_CUR);
696 if (r) {
697 DEBUG(writer->ctx, "Could not seek to position after header\n");
698 return r;
699 }
700 offset += sizeof(header);
701
702 align_page_boundary(&offset, f);
703
704 // Write all ASes
705 r = loc_database_write_as_section(writer, &header, &offset, f);
706 if (r)
707 return r;
708
f3e02bc5 709 // Write all networks
438db08c 710 r = loc_database_write_networks(writer, &header, &offset, f);
f3e02bc5
MT
711 if (r)
712 return r;
713
47bea941
MT
714 // Write countries
715 r = loc_database_write_countries(writer, &header, &offset, f);
c182393f
MT
716 if (r)
717 return r;
718
47bea941
MT
719 // Write pool
720 r = loc_database_write_pool(writer, &header, &offset, f);
ec684c1a
MT
721 if (r)
722 return r;
723
5ce881d4
MT
724 // Create the signatures
725 if (writer->private_key1) {
726 DEBUG(writer->ctx, "Creating signature with first private key\n");
727
728 writer->signature1_length = sizeof(writer->signature1);
729
730 r = loc_writer_create_signature(writer, &header, f,
731 writer->private_key1, writer->signature1, &writer->signature1_length);
726f9984
MT
732 if (r)
733 return r;
734 }
735
5ce881d4
MT
736 if (writer->private_key2) {
737 DEBUG(writer->ctx, "Creating signature with second private key\n");
738
739 writer->signature2_length = sizeof(writer->signature2);
740
741 r = loc_writer_create_signature(writer, &header, f,
742 writer->private_key2, writer->signature2, &writer->signature2_length);
743 if (r)
744 return r;
745 }
746
747 // Copy the signatures into the header
748 if (writer->signature1_length) {
c7db968a
MT
749 DEBUG(writer->ctx, "Copying first signature of %zu byte(s)\n",
750 writer->signature1_length);
751
5ce881d4 752 memcpy(header.signature1, writer->signature1, writer->signature1_length);
c7db968a 753 header.signature1_length = htobe16(writer->signature1_length);
5ce881d4
MT
754 }
755
756 if (writer->signature2_length) {
c7db968a 757 DEBUG(writer->ctx, "Copying second signature of %zu byte(s)\n",
354674e7 758 writer->signature2_length);
c7db968a 759
5ce881d4 760 memcpy(header.signature2, writer->signature2, writer->signature2_length);
c7db968a 761 header.signature2_length = htobe16(writer->signature2_length);
5ce881d4
MT
762 }
763
c182393f
MT
764 // Write the header
765 r = fseek(f, sizeof(magic), SEEK_SET);
766 if (r)
767 return r;
768
726f9984 769 fwrite(&header, 1, sizeof(header), f);
c182393f 770
52580c03
MT
771 // Flush everything
772 fflush(f);
773
726f9984 774 return r;
c182393f 775}