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