]> git.ipfire.org Git - location/libloc.git/blob - src/writer.c
writer: Free array with pointer to ASes, too
[location/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 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);
155 }
156
157 // Release network tree
158 if (writer->networks)
159 loc_network_tree_unref(writer->networks);
160
161 // Unref the string pool
162 loc_stringpool_unref(writer->pool);
163
164 loc_unref(writer->ctx);
165 free(writer);
166 }
167
168 LOC_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
177 LOC_EXPORT const char* loc_writer_get_vendor(struct loc_writer* writer) {
178 return loc_stringpool_get(writer->pool, writer->vendor);
179 }
180
181 LOC_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
191 LOC_EXPORT const char* loc_writer_get_description(struct loc_writer* writer) {
192 return loc_stringpool_get(writer->pool, writer->description);
193 }
194
195 LOC_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
205 LOC_EXPORT const char* loc_writer_get_license(struct loc_writer* writer) {
206 return loc_stringpool_get(writer->pool, writer->license);
207 }
208
209 LOC_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
219 static 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
223 LOC_EXPORT int loc_writer_add_as(struct loc_writer* writer, struct loc_as** as, uint32_t number) {
224 int r = loc_as_new(writer->ctx, as, number);
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
245 LOC_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
257 static 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
261 LOC_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
283 static void make_magic(struct loc_writer* writer, struct loc_database_magic* magic,
284 enum loc_database_version version) {
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
290 magic->version = version;
291 }
292
293 static 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
299 static int loc_database_write_pool(struct loc_writer* writer,
300 struct loc_database_header_v1* header, off_t* offset, FILE* f) {
301 // Save the offset of the pool section
302 DEBUG(writer->ctx, "Pool starts at %jd bytes\n", (intmax_t)*offset);
303 header->pool_offset = htobe32(*offset);
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);
310 header->pool_length = htobe32(pool_length);
311
312 return 0;
313 }
314
315 static int loc_database_write_as_section(struct loc_writer* writer,
316 struct loc_database_header_v1* header, off_t* offset, FILE* f) {
317 DEBUG(writer->ctx, "AS section starts at %jd bytes\n", (intmax_t)*offset);
318 header->as_offset = htobe32(*offset);
319
320 size_t as_length = 0;
321
322 struct loc_database_as_v1 as;
323 for (unsigned int i = 0; i < writer->as_count; i++) {
324 // Convert AS into database format
325 loc_as_to_database_v1(writer->as[i], writer->pool, &as);
326
327 // Write to disk
328 *offset += fwrite(&as, 1, sizeof(as), f);
329 as_length += sizeof(as);
330 }
331
332 DEBUG(writer->ctx, "AS section has a length of %zu bytes\n", as_length);
333 header->as_length = htobe32(as_length);
334
335 align_page_boundary(offset, f);
336
337 return 0;
338 }
339
340 struct node {
341 TAILQ_ENTRY(node) nodes;
342
343 struct loc_network_tree_node* node;
344
345 // Indices of the child nodes
346 uint32_t index_zero;
347 uint32_t index_one;
348 };
349
350 static struct node* make_node(struct loc_network_tree_node* node) {
351 struct node* n = malloc(sizeof(*n));
352 if (!n)
353 return NULL;
354
355 n->node = loc_network_tree_node_ref(node);
356 n->index_zero = n->index_one = 0;
357
358 return n;
359 }
360
361 static void free_node(struct node* node) {
362 loc_network_tree_node_unref(node->node);
363
364 free(node);
365 }
366
367 struct network {
368 TAILQ_ENTRY(network) networks;
369
370 struct loc_network* network;
371 };
372
373 static 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;
381 }
382
383 static void free_network(struct network* network) {
384 loc_network_unref(network->network);
385
386 free(network);
387 }
388
389 static int loc_database_write_networks(struct loc_writer* writer,
390 struct loc_database_header_v1* header, off_t* offset, FILE* f) {
391 // Write the network tree
392 DEBUG(writer->ctx, "Network tree starts at %jd bytes\n", (intmax_t)*offset);
393 header->network_tree_offset = htobe32(*offset);
394
395 size_t network_tree_length = 0;
396 size_t network_data_length = 0;
397
398 struct node* node;
399 struct node* child_node;
400
401 uint32_t index = 0;
402 uint32_t network_index = 0;
403
404 struct loc_database_network_v1 db_network;
405 struct loc_database_network_node_v1 db_node;
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
450 db_node.zero = htobe32(node->index_zero);
451 db_node.one = htobe32(node->index_one);
452
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
460 db_node.network = htobe32(network_index++);
461 loc_network_unref(network);
462 } else {
463 db_node.network = htobe32(0xffffffff);
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
482 DEBUG(writer->ctx, "Networks data section starts at %jd bytes\n", (intmax_t)*offset);
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
492 int r = loc_network_to_database_v1(nw->network, &db_network);
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 }
501
502 header->network_data_length = htobe32(network_data_length);
503
504 align_page_boundary(offset, f);
505
506 return 0;
507 }
508
509 static int loc_database_write_countries(struct loc_writer* writer,
510 struct loc_database_header_v1* header, off_t* offset, FILE* f) {
511 DEBUG(writer->ctx, "Countries section starts at %jd bytes\n", (intmax_t)*offset);
512 header->countries_offset = htobe32(*offset);
513
514 size_t countries_length = 0;
515
516 struct loc_database_country_v1 country;
517 for (unsigned int i = 0; i < writer->countries_count; i++) {
518 // Convert country into database format
519 loc_country_to_database_v1(writer->countries[i], writer->pool, &country);
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
534 static int loc_writer_create_signature(struct loc_writer* writer,
535 struct loc_database_header_v1* header, FILE* f, EVP_PKEY* private_key,
536 char* signature, size_t* length) {
537 DEBUG(writer->ctx, "Creating signature...\n");
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
546 int r = EVP_DigestSignInit(mdctx, NULL, NULL, NULL, private_key);
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
556 hexdump(writer->ctx, &magic, sizeof(magic));
557
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
565 hexdump(writer->ctx, header, sizeof(*header));
566
567 // Feed the header into the signature
568 r = EVP_DigestSignUpdate(mdctx, header, sizeof(*header));
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
575 fseek(f, sizeof(*header), SEEK_CUR);
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
588 hexdump(writer->ctx, buffer, bytes_read);
589
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));
593 r = -1;
594 goto END;
595 }
596 }
597
598 // Compute the signature
599 r = EVP_DigestSignFinal(mdctx,
600 (unsigned char*)signature, length);
601 if (r != 1) {
602 ERROR(writer->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
603 r = -1;
604 goto END;
605 }
606
607 DEBUG(writer->ctx, "Successfully generated signature of %lu bytes\n", *length);
608 r = 0;
609
610 // Dump signature
611 hexdump(writer->ctx, signature, *length);
612
613 END:
614 EVP_MD_CTX_free(mdctx);
615
616 return r;
617 }
618
619 LOC_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
636 struct loc_database_magic magic;
637 make_magic(writer, &magic, version);
638
639 // Make the header
640 struct loc_database_header_v1 header;
641 header.vendor = htobe32(writer->vendor);
642 header.description = htobe32(writer->description);
643 header.license = htobe32(writer->license);
644
645 time_t now = time(NULL);
646 header.created_at = htobe64(now);
647
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;
653
654 // Clear the padding
655 memset(header.padding, '\0', sizeof(header.padding));
656
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
683 // Write all networks
684 r = loc_database_write_networks(writer, &header, &offset, f);
685 if (r)
686 return r;
687
688 // Write countries
689 r = loc_database_write_countries(writer, &header, &offset, f);
690 if (r)
691 return r;
692
693 // Write pool
694 r = loc_database_write_pool(writer, &header, &offset, f);
695 if (r)
696 return r;
697
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);
706 if (r)
707 return r;
708 }
709
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) {
723 DEBUG(writer->ctx, "Copying first signature of %zu byte(s)\n",
724 writer->signature1_length);
725
726 memcpy(header.signature1, writer->signature1, writer->signature1_length);
727 header.signature1_length = htobe16(writer->signature1_length);
728 }
729
730 if (writer->signature2_length) {
731 DEBUG(writer->ctx, "Copying second signature of %zu byte(s)\n",
732 writer->signature1_length);
733
734 memcpy(header.signature2, writer->signature2, writer->signature2_length);
735 header.signature2_length = htobe16(writer->signature2_length);
736 }
737
738 // Write the header
739 r = fseek(f, sizeof(magic), SEEK_SET);
740 if (r)
741 return r;
742
743 fwrite(&header, 1, sizeof(header), f);
744
745 return r;
746 }