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