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