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