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