]> git.ipfire.org Git - people/ms/libloc.git/blob - src/writer.c
5939cff0fd5cc42ec20e16a10a7c44b89dcb4ad1
[people/ms/libloc.git] / src / writer.c
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
17 #include <assert.h>
18 #include <errno.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/queue.h>
23 #include <time.h>
24
25 #ifdef HAVE_ENDIAN_H
26 # include <endian.h>
27 #endif
28
29 #include <openssl/bio.h>
30 #include <openssl/err.h>
31 #include <openssl/evp.h>
32 #include <openssl/pem.h>
33
34 #include <loc/libloc.h>
35 #include <loc/as.h>
36 #include <loc/compat.h>
37 #include <loc/country.h>
38 #include <loc/database.h>
39 #include <loc/format.h>
40 #include <loc/network.h>
41 #include <loc/private.h>
42 #include <loc/writer.h>
43
44 struct loc_writer {
45 struct loc_ctx* ctx;
46 int refcount;
47
48 struct loc_stringpool* pool;
49 off_t vendor;
50 off_t description;
51 off_t license;
52
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;
62
63 struct loc_as** as;
64 size_t as_count;
65
66 struct loc_country** countries;
67 size_t countries_count;
68
69 struct loc_network_tree* networks;
70 };
71
72 static int parse_private_key(struct loc_writer* writer, EVP_PKEY** private_key, FILE* f) {
73 // Free any previously loaded keys
74 if (*private_key)
75 EVP_PKEY_free(*private_key);
76
77 // Read the key
78 *private_key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
79
80 // Log any errors
81 if (!*private_key) {
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
91 LOC_EXPORT int loc_writer_new(struct loc_ctx* ctx, struct loc_writer** writer,
92 FILE* fkey1, FILE* fkey2) {
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
100 int r = loc_stringpool_new(ctx, &w->pool);
101 if (r) {
102 loc_writer_unref(w);
103 return r;
104 }
105
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
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);
124 if (r) {
125 loc_writer_unref(w);
126 return r;
127 }
128 }
129
130 *writer = w;
131 return 0;
132 }
133
134 LOC_EXPORT struct loc_writer* loc_writer_ref(struct loc_writer* writer) {
135 writer->refcount++;
136
137 return writer;
138 }
139
140 static void loc_writer_free(struct loc_writer* writer) {
141 DEBUG(writer->ctx, "Releasing writer at %p\n", writer);
142
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);
148
149 // Unref all AS
150 for (unsigned int i = 0; i < writer->as_count; i++) {
151 loc_as_unref(writer->as[i]);
152 }
153
154 // Release network tree
155 if (writer->networks)
156 loc_network_tree_unref(writer->networks);
157
158 // Unref the string pool
159 loc_stringpool_unref(writer->pool);
160
161 loc_unref(writer->ctx);
162 free(writer);
163 }
164
165 LOC_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
174 LOC_EXPORT const char* loc_writer_get_vendor(struct loc_writer* writer) {
175 return loc_stringpool_get(writer->pool, writer->vendor);
176 }
177
178 LOC_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
188 LOC_EXPORT const char* loc_writer_get_description(struct loc_writer* writer) {
189 return loc_stringpool_get(writer->pool, writer->description);
190 }
191
192 LOC_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
202 LOC_EXPORT const char* loc_writer_get_license(struct loc_writer* writer) {
203 return loc_stringpool_get(writer->pool, writer->license);
204 }
205
206 LOC_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
216 static 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
220 LOC_EXPORT int loc_writer_add_as(struct loc_writer* writer, struct loc_as** as, uint32_t number) {
221 int r = loc_as_new(writer->ctx, as, number);
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
242 LOC_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
254 static 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
258 LOC_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
280 static void make_magic(struct loc_writer* writer, struct loc_database_magic* magic,
281 enum loc_database_version version) {
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
287 magic->version = version;
288 }
289
290 static 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
296 static int loc_database_write_pool(struct loc_writer* writer,
297 struct loc_database_header_v1* header, off_t* offset, FILE* f) {
298 // Save the offset of the pool section
299 DEBUG(writer->ctx, "Pool starts at %jd bytes\n", (intmax_t)*offset);
300 header->pool_offset = htobe32(*offset);
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);
307 header->pool_length = htobe32(pool_length);
308
309 return 0;
310 }
311
312 static int loc_database_write_as_section(struct loc_writer* writer,
313 struct loc_database_header_v1* header, off_t* offset, FILE* f) {
314 DEBUG(writer->ctx, "AS section starts at %jd bytes\n", (intmax_t)*offset);
315 header->as_offset = htobe32(*offset);
316
317 size_t as_length = 0;
318
319 struct loc_database_as_v1 as;
320 for (unsigned int i = 0; i < writer->as_count; i++) {
321 // Convert AS into database format
322 loc_as_to_database_v1(writer->as[i], writer->pool, &as);
323
324 // Write to disk
325 *offset += fwrite(&as, 1, sizeof(as), f);
326 as_length += sizeof(as);
327 }
328
329 DEBUG(writer->ctx, "AS section has a length of %zu bytes\n", as_length);
330 header->as_length = htobe32(as_length);
331
332 align_page_boundary(offset, f);
333
334 return 0;
335 }
336
337 struct node {
338 TAILQ_ENTRY(node) nodes;
339
340 struct loc_network_tree_node* node;
341
342 // Indices of the child nodes
343 uint32_t index_zero;
344 uint32_t index_one;
345 };
346
347 static struct node* make_node(struct loc_network_tree_node* node) {
348 struct node* n = malloc(sizeof(*n));
349 if (!n)
350 return NULL;
351
352 n->node = loc_network_tree_node_ref(node);
353 n->index_zero = n->index_one = 0;
354
355 return n;
356 }
357
358 static void free_node(struct node* node) {
359 loc_network_tree_node_unref(node->node);
360
361 free(node);
362 }
363
364 struct network {
365 TAILQ_ENTRY(network) networks;
366
367 struct loc_network* network;
368 };
369
370 static 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;
378 }
379
380 static void free_network(struct network* network) {
381 loc_network_unref(network->network);
382
383 free(network);
384 }
385
386 static int loc_database_write_networks(struct loc_writer* writer,
387 struct loc_database_header_v1* header, off_t* offset, FILE* f) {
388 // Write the network tree
389 DEBUG(writer->ctx, "Network tree starts at %jd bytes\n", (intmax_t)*offset);
390 header->network_tree_offset = htobe32(*offset);
391
392 size_t network_tree_length = 0;
393 size_t network_data_length = 0;
394
395 struct node* node;
396 struct node* child_node;
397
398 uint32_t index = 0;
399 uint32_t network_index = 0;
400
401 struct loc_database_network_v1 db_network;
402 struct loc_database_network_node_v1 db_node;
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
447 db_node.zero = htobe32(node->index_zero);
448 db_node.one = htobe32(node->index_one);
449
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
457 db_node.network = htobe32(network_index++);
458 loc_network_unref(network);
459 } else {
460 db_node.network = htobe32(0xffffffff);
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
479 DEBUG(writer->ctx, "Networks data section starts at %jd bytes\n", (intmax_t)*offset);
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
489 int r = loc_network_to_database_v1(nw->network, &db_network);
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 }
498
499 header->network_data_length = htobe32(network_data_length);
500
501 align_page_boundary(offset, f);
502
503 return 0;
504 }
505
506 static int loc_database_write_countries(struct loc_writer* writer,
507 struct loc_database_header_v1* header, off_t* offset, FILE* f) {
508 DEBUG(writer->ctx, "Countries section starts at %jd bytes\n", (intmax_t)*offset);
509 header->countries_offset = htobe32(*offset);
510
511 size_t countries_length = 0;
512
513 struct loc_database_country_v1 country;
514 for (unsigned int i = 0; i < writer->countries_count; i++) {
515 // Convert country into database format
516 loc_country_to_database_v1(writer->countries[i], writer->pool, &country);
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
531 static int loc_writer_create_signature(struct loc_writer* writer,
532 struct loc_database_header_v1* header, FILE* f, EVP_PKEY* private_key,
533 char* signature, size_t* length) {
534 DEBUG(writer->ctx, "Creating signature...\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
543 int r = EVP_DigestSignInit(mdctx, NULL, NULL, NULL, private_key);
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
553 hexdump(writer->ctx, &magic, sizeof(magic));
554
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
562 hexdump(writer->ctx, header, sizeof(*header));
563
564 // Feed the header into the signature
565 r = EVP_DigestSignUpdate(mdctx, header, sizeof(*header));
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
572 fseek(f, sizeof(*header), SEEK_CUR);
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
585 hexdump(writer->ctx, buffer, bytes_read);
586
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));
590 r = -1;
591 goto END;
592 }
593 }
594
595 // Compute the signature
596 r = EVP_DigestSignFinal(mdctx,
597 (unsigned char*)signature, length);
598 if (r != 1) {
599 ERROR(writer->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
600 r = -1;
601 goto END;
602 }
603
604 DEBUG(writer->ctx, "Successfully generated signature of %lu bytes\n", *length);
605 r = 0;
606
607 // Dump signature
608 hexdump(writer->ctx, signature, *length);
609
610 END:
611 EVP_MD_CTX_free(mdctx);
612
613 return r;
614 }
615
616 LOC_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
633 struct loc_database_magic magic;
634 make_magic(writer, &magic, version);
635
636 // Make the header
637 struct loc_database_header_v1 header;
638 header.vendor = htobe32(writer->vendor);
639 header.description = htobe32(writer->description);
640 header.license = htobe32(writer->license);
641
642 time_t now = time(NULL);
643 header.created_at = htobe64(now);
644
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;
650
651 // Clear the padding
652 memset(header.padding, '\0', sizeof(header.padding));
653
654 int r;
655 off_t offset = 0;
656
657 // Start writing at the beginning of the file
658 r = fseek(f, 0, SEEK_SET);
659 if (r)
660 return r;
661
662 // Write the magic
663 offset += fwrite(&magic, 1, sizeof(magic), f);
664
665 // Skip the space we need to write the header later
666 r = fseek(f, sizeof(header), SEEK_CUR);
667 if (r) {
668 DEBUG(writer->ctx, "Could not seek to position after header\n");
669 return r;
670 }
671 offset += sizeof(header);
672
673 align_page_boundary(&offset, f);
674
675 // Write all ASes
676 r = loc_database_write_as_section(writer, &header, &offset, f);
677 if (r)
678 return r;
679
680 // Write all networks
681 r = loc_database_write_networks(writer, &header, &offset, f);
682 if (r)
683 return r;
684
685 // Write countries
686 r = loc_database_write_countries(writer, &header, &offset, f);
687 if (r)
688 return r;
689
690 // Write pool
691 r = loc_database_write_pool(writer, &header, &offset, f);
692 if (r)
693 return r;
694
695 // Create the signatures
696 if (writer->private_key1) {
697 DEBUG(writer->ctx, "Creating signature with first private key\n");
698
699 writer->signature1_length = sizeof(writer->signature1);
700
701 r = loc_writer_create_signature(writer, &header, f,
702 writer->private_key1, writer->signature1, &writer->signature1_length);
703 if (r)
704 return r;
705 }
706
707 if (writer->private_key2) {
708 DEBUG(writer->ctx, "Creating signature with second private key\n");
709
710 writer->signature2_length = sizeof(writer->signature2);
711
712 r = loc_writer_create_signature(writer, &header, f,
713 writer->private_key2, writer->signature2, &writer->signature2_length);
714 if (r)
715 return r;
716 }
717
718 // Copy the signatures into the header
719 if (writer->signature1_length) {
720 DEBUG(writer->ctx, "Copying first signature of %zu byte(s)\n",
721 writer->signature1_length);
722
723 memcpy(header.signature1, writer->signature1, writer->signature1_length);
724 header.signature1_length = htobe16(writer->signature1_length);
725 }
726
727 if (writer->signature2_length) {
728 DEBUG(writer->ctx, "Copying second signature of %zu byte(s)\n",
729 writer->signature1_length);
730
731 memcpy(header.signature2, writer->signature2, writer->signature2_length);
732 header.signature2_length = htobe16(writer->signature2_length);
733 }
734
735 // Write the header
736 r = fseek(f, sizeof(magic), SEEK_SET);
737 if (r)
738 return r;
739
740 fwrite(&header, 1, sizeof(header), f);
741
742 return r;
743 }