2 libloc - A library to determine the location of someone on the Internet
4 Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
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.
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.
22 #include <sys/queue.h>
29 #include <openssl/bio.h>
30 #include <openssl/err.h>
31 #include <openssl/evp.h>
32 #include <openssl/pem.h>
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/country-list.h>
40 #include <libloc/database.h>
41 #include <libloc/format.h>
42 #include <libloc/network.h>
43 #include <libloc/network-tree.h>
44 #include <libloc/private.h>
45 #include <libloc/writer.h>
51 struct loc_stringpool
* pool
;
56 // Private keys to sign any databases
57 EVP_PKEY
* private_key1
;
58 EVP_PKEY
* private_key2
;
61 char signature1
[LOC_SIGNATURE_MAX_LENGTH
];
62 size_t signature1_length
;
63 char signature2
[LOC_SIGNATURE_MAX_LENGTH
];
64 size_t signature2_length
;
66 struct loc_network_tree
* networks
;
68 struct loc_as_list
* as_list
;
69 struct loc_country_list
* country_list
;
72 static int parse_private_key(struct loc_writer
* writer
, EVP_PKEY
** private_key
, FILE* f
) {
73 // Free any previously loaded keys
75 EVP_PKEY_free(*private_key
);
78 *private_key
= PEM_read_PrivateKey(f
, NULL
, NULL
, NULL
);
82 char* error
= ERR_error_string(ERR_get_error(), NULL
);
83 ERROR(writer
->ctx
, "Could not parse private key: %s\n", error
);
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
));
97 w
->ctx
= loc_ref(ctx
);
100 int r
= loc_stringpool_new(ctx
, &w
->pool
);
106 // Add an empty string to the stringpool
107 r
= loc_stringpool_add(w
->pool
, "");
113 // Initialize the network tree
114 r
= loc_network_tree_new(ctx
, &w
->networks
);
120 // Initialize AS list
121 r
= loc_as_list_new(ctx
, &w
->as_list
);
127 // Initialize countries list
128 r
= loc_country_list_new(ctx
, &w
->country_list
);
134 // Load the private keys to sign databases
136 r
= parse_private_key(w
, &w
->private_key1
, fkey1
);
144 r
= parse_private_key(w
, &w
->private_key2
, fkey2
);
155 LOC_EXPORT
struct loc_writer
* loc_writer_ref(struct loc_writer
* writer
) {
161 static void loc_writer_free(struct loc_writer
* writer
) {
162 DEBUG(writer
->ctx
, "Releasing writer at %p\n", writer
);
165 if (writer
->private_key1
)
166 EVP_PKEY_free(writer
->private_key1
);
167 if (writer
->private_key2
)
168 EVP_PKEY_free(writer
->private_key2
);
172 loc_as_list_unref(writer
->as_list
);
174 // Unref all countries
175 if (writer
->country_list
)
176 loc_country_list_unref(writer
->country_list
);
178 // Release network tree
179 if (writer
->networks
)
180 loc_network_tree_unref(writer
->networks
);
182 // Unref the string pool
184 loc_stringpool_unref(writer
->pool
);
186 loc_unref(writer
->ctx
);
190 LOC_EXPORT
struct loc_writer
* loc_writer_unref(struct loc_writer
* writer
) {
191 if (--writer
->refcount
> 0)
194 loc_writer_free(writer
);
199 LOC_EXPORT
const char* loc_writer_get_vendor(struct loc_writer
* writer
) {
200 return loc_stringpool_get(writer
->pool
, writer
->vendor
);
203 LOC_EXPORT
int loc_writer_set_vendor(struct loc_writer
* writer
, const char* vendor
) {
204 // Add the string to the string pool
205 off_t offset
= loc_stringpool_add(writer
->pool
, vendor
);
209 writer
->vendor
= offset
;
213 LOC_EXPORT
const char* loc_writer_get_description(struct loc_writer
* writer
) {
214 return loc_stringpool_get(writer
->pool
, writer
->description
);
217 LOC_EXPORT
int loc_writer_set_description(struct loc_writer
* writer
, const char* description
) {
218 // Add the string to the string pool
219 off_t offset
= loc_stringpool_add(writer
->pool
, description
);
223 writer
->description
= offset
;
227 LOC_EXPORT
const char* loc_writer_get_license(struct loc_writer
* writer
) {
228 return loc_stringpool_get(writer
->pool
, writer
->license
);
231 LOC_EXPORT
int loc_writer_set_license(struct loc_writer
* writer
, const char* license
) {
232 // Add the string to the string pool
233 off_t offset
= loc_stringpool_add(writer
->pool
, license
);
237 writer
->license
= offset
;
241 LOC_EXPORT
int loc_writer_add_as(struct loc_writer
* writer
, struct loc_as
** as
, uint32_t number
) {
242 // Create a new AS object
243 int r
= loc_as_new(writer
->ctx
, as
, number
);
247 // Append it to the list
248 return loc_as_list_append(writer
->as_list
, *as
);
251 LOC_EXPORT
int loc_writer_add_network(struct loc_writer
* writer
, struct loc_network
** network
, const char* string
) {
254 // Create a new network object
255 r
= loc_network_new_from_string(writer
->ctx
, network
, string
);
259 // Add it to the local tree
260 return loc_network_tree_add_network(writer
->networks
, *network
);
263 LOC_EXPORT
int loc_writer_add_country(struct loc_writer
* writer
, struct loc_country
** country
, const char* country_code
) {
264 // Allocate a new country
265 int r
= loc_country_new(writer
->ctx
, country
, country_code
);
269 // Append it to the list
270 return loc_country_list_append(writer
->country_list
, *country
);
273 static void make_magic(struct loc_writer
* writer
, struct loc_database_magic
* magic
,
274 enum loc_database_version version
) {
276 for (unsigned int i
= 0; i
< strlen(LOC_DATABASE_MAGIC
); i
++)
277 magic
->magic
[i
] = LOC_DATABASE_MAGIC
[i
];
280 magic
->version
= version
;
283 static void align_page_boundary(off_t
* offset
, FILE* f
) {
284 // Move to next page boundary
285 while (*offset
% LOC_DATABASE_PAGE_SIZE
> 0)
286 *offset
+= fwrite("", 1, 1, f
);
289 static int loc_database_write_pool(struct loc_writer
* writer
,
290 struct loc_database_header_v1
* header
, off_t
* offset
, FILE* f
) {
291 // Save the offset of the pool section
292 DEBUG(writer
->ctx
, "Pool starts at %jd bytes\n", (intmax_t)*offset
);
293 header
->pool_offset
= htobe32(*offset
);
296 size_t pool_length
= loc_stringpool_write(writer
->pool
, f
);
297 *offset
+= pool_length
;
299 DEBUG(writer
->ctx
, "Pool has a length of %zu bytes\n", pool_length
);
300 header
->pool_length
= htobe32(pool_length
);
305 static int loc_database_write_as_section(struct loc_writer
* writer
,
306 struct loc_database_header_v1
* header
, off_t
* offset
, FILE* f
) {
307 DEBUG(writer
->ctx
, "AS section starts at %jd bytes\n", (intmax_t)*offset
);
308 header
->as_offset
= htobe32(*offset
);
310 // Sort the AS list first
311 loc_as_list_sort(writer
->as_list
);
313 const size_t as_count
= loc_as_list_size(writer
->as_list
);
315 struct loc_database_as_v1 block
;
316 size_t block_length
= 0;
318 for (unsigned int i
= 0; i
< as_count
; i
++) {
319 struct loc_as
* as
= loc_as_list_get(writer
->as_list
, i
);
323 // Convert AS into database format
324 loc_as_to_database_v1(as
, writer
->pool
, &block
);
327 *offset
+= fwrite(&block
, 1, sizeof(block
), f
);
328 block_length
+= sizeof(block
);
334 DEBUG(writer
->ctx
, "AS section has a length of %zu bytes\n", block_length
);
335 header
->as_length
= htobe32(block_length
);
337 align_page_boundary(offset
, f
);
343 TAILQ_ENTRY(node
) nodes
;
345 struct loc_network_tree_node
* node
;
347 // Indices of the child nodes
352 static struct node
* make_node(struct loc_network_tree_node
* node
) {
353 struct node
* n
= malloc(sizeof(*n
));
357 n
->node
= loc_network_tree_node_ref(node
);
358 n
->index_zero
= n
->index_one
= 0;
363 static void free_node(struct node
* node
) {
364 loc_network_tree_node_unref(node
->node
);
370 TAILQ_ENTRY(network
) networks
;
372 struct loc_network
* network
;
375 static struct network
* make_network(struct loc_network
* network
) {
376 struct network
* n
= malloc(sizeof(*n
));
380 n
->network
= loc_network_ref(network
);
385 static void free_network(struct network
* network
) {
386 loc_network_unref(network
->network
);
391 static int loc_database_write_networks(struct loc_writer
* writer
,
392 struct loc_database_header_v1
* header
, off_t
* offset
, FILE* f
) {
395 // Write the network tree
396 DEBUG(writer
->ctx
, "Network tree starts at %jd bytes\n", (intmax_t)*offset
);
397 header
->network_tree_offset
= htobe32(*offset
);
399 size_t network_tree_length
= 0;
400 size_t network_data_length
= 0;
403 struct node
* child_node
;
406 uint32_t network_index
= 0;
408 struct loc_database_network_v1 db_network
;
409 struct loc_database_network_node_v1 db_node
;
411 // Initialize queue for nodes
412 TAILQ_HEAD(node_t
, node
) nodes
;
415 // Initialize queue for networks
416 TAILQ_HEAD(network_t
, network
) networks
;
417 TAILQ_INIT(&networks
);
419 // Cleanup the tree before writing it
420 r
= loc_network_tree_cleanup(writer
->networks
);
425 struct loc_network_tree_node
* root
= loc_network_tree_get_root(writer
->networks
);
426 node
= make_node(root
);
430 TAILQ_INSERT_TAIL(&nodes
, node
, nodes
);
432 while (!TAILQ_EMPTY(&nodes
)) {
433 // Pop first node in list
434 node
= TAILQ_FIRST(&nodes
);
435 TAILQ_REMOVE(&nodes
, node
, nodes
);
437 DEBUG(writer
->ctx
, "Processing node %p\n", node
);
440 struct loc_network_tree_node
* node_zero
= loc_network_tree_node_get(node
->node
, 0);
442 node
->index_zero
= ++index
;
444 child_node
= make_node(node_zero
);
445 loc_network_tree_node_unref(node_zero
);
447 TAILQ_INSERT_TAIL(&nodes
, child_node
, nodes
);
450 struct loc_network_tree_node
* node_one
= loc_network_tree_node_get(node
->node
, 1);
452 node
->index_one
= ++index
;
454 child_node
= make_node(node_one
);
455 loc_network_tree_node_unref(node_one
);
457 TAILQ_INSERT_TAIL(&nodes
, child_node
, nodes
);
460 // Prepare what we are writing to disk
461 db_node
.zero
= htobe32(node
->index_zero
);
462 db_node
.one
= htobe32(node
->index_one
);
464 if (loc_network_tree_node_is_leaf(node
->node
)) {
465 struct loc_network
* network
= loc_network_tree_node_get_network(node
->node
);
467 // Append network to be written out later
468 struct network
* nw
= make_network(network
);
473 TAILQ_INSERT_TAIL(&networks
, nw
, networks
);
475 db_node
.network
= htobe32(network_index
++);
476 loc_network_unref(network
);
478 db_node
.network
= htobe32(0xffffffff);
481 // Write the current node
482 DEBUG(writer
->ctx
, "Writing node %p (0 = %d, 1 = %d)\n",
483 node
, node
->index_zero
, node
->index_one
);
485 *offset
+= fwrite(&db_node
, 1, sizeof(db_node
), f
);
486 network_tree_length
+= sizeof(db_node
);
491 loc_network_tree_node_unref(root
);
493 header
->network_tree_length
= htobe32(network_tree_length
);
495 align_page_boundary(offset
, f
);
497 DEBUG(writer
->ctx
, "Networks data section starts at %jd bytes\n", (intmax_t)*offset
);
498 header
->network_data_offset
= htobe32(*offset
);
500 // We have now written the entire tree and have all networks
501 // in a queue in order as they are indexed
502 while (!TAILQ_EMPTY(&networks
)) {
503 struct network
* nw
= TAILQ_FIRST(&networks
);
504 TAILQ_REMOVE(&networks
, nw
, networks
);
506 // Prepare what we are writing to disk
507 r
= loc_network_to_database_v1(nw
->network
, &db_network
);
511 *offset
+= fwrite(&db_network
, 1, sizeof(db_network
), f
);
512 network_data_length
+= sizeof(db_network
);
517 header
->network_data_length
= htobe32(network_data_length
);
519 align_page_boundary(offset
, f
);
524 static int loc_database_write_countries(struct loc_writer
* writer
,
525 struct loc_database_header_v1
* header
, off_t
* offset
, FILE* f
) {
526 DEBUG(writer
->ctx
, "Countries section starts at %jd bytes\n", (intmax_t)*offset
);
527 header
->countries_offset
= htobe32(*offset
);
529 const size_t countries_count
= loc_country_list_size(writer
->country_list
);
531 struct loc_database_country_v1 block
;
532 size_t block_length
= 0;
534 for (unsigned int i
= 0; i
< countries_count
; i
++) {
535 struct loc_country
* country
= loc_country_list_get(writer
->country_list
, i
);
537 // Convert country into database format
538 loc_country_to_database_v1(country
, writer
->pool
, &block
);
541 *offset
+= fwrite(&block
, 1, sizeof(block
), f
);
542 block_length
+= sizeof(block
);
545 DEBUG(writer
->ctx
, "Countries section has a length of %zu bytes\n", block_length
);
546 header
->countries_length
= htobe32(block_length
);
548 align_page_boundary(offset
, f
);
553 static int loc_writer_create_signature(struct loc_writer
* writer
,
554 struct loc_database_header_v1
* header
, FILE* f
, EVP_PKEY
* private_key
,
555 char* signature
, size_t* length
) {
556 size_t bytes_read
= 0;
558 DEBUG(writer
->ctx
, "Creating signature...\n");
560 // Read file from the beginning
563 // Create a new context for signing
564 EVP_MD_CTX
* mdctx
= EVP_MD_CTX_new();
566 // Initialise the context
567 int r
= EVP_DigestSignInit(mdctx
, NULL
, NULL
, NULL
, private_key
);
569 ERROR(writer
->ctx
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
574 struct loc_database_magic magic
;
575 bytes_read
= fread(&magic
, 1, sizeof(magic
), f
);
576 if (bytes_read
< sizeof(magic
)) {
577 ERROR(writer
->ctx
, "Could not read header: %m\n");
582 hexdump(writer
->ctx
, &magic
, sizeof(magic
));
584 // Feed magic into the signature
585 r
= EVP_DigestSignUpdate(mdctx
, &magic
, sizeof(magic
));
587 ERROR(writer
->ctx
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
591 hexdump(writer
->ctx
, header
, sizeof(*header
));
593 // Feed the header into the signature
594 r
= EVP_DigestSignUpdate(mdctx
, header
, sizeof(*header
));
596 ERROR(writer
->ctx
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
601 fseek(f
, sizeof(*header
), SEEK_CUR
);
603 // Walk through the file in chunks of 64kB
604 char buffer
[64 * 1024];
606 bytes_read
= fread(buffer
, 1, sizeof(buffer
), f
);
609 ERROR(writer
->ctx
, "Error reading from file: %m\n");
614 hexdump(writer
->ctx
, buffer
, bytes_read
);
616 r
= EVP_DigestSignUpdate(mdctx
, buffer
, bytes_read
);
618 ERROR(writer
->ctx
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
624 // Compute the signature
625 r
= EVP_DigestSignFinal(mdctx
,
626 (unsigned char*)signature
, length
);
628 ERROR(writer
->ctx
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
633 DEBUG(writer
->ctx
, "Successfully generated signature of %zu bytes\n", *length
);
637 hexdump(writer
->ctx
, signature
, *length
);
640 EVP_MD_CTX_free(mdctx
);
645 LOC_EXPORT
int loc_writer_write(struct loc_writer
* writer
, FILE* f
, enum loc_database_version version
) {
648 case LOC_DATABASE_VERSION_UNSET
:
649 version
= LOC_DATABASE_VERSION_LATEST
;
652 case LOC_DATABASE_VERSION_1
:
656 ERROR(writer
->ctx
, "Invalid database version: %d\n", version
);
660 DEBUG(writer
->ctx
, "Writing database in version %d\n", version
);
662 struct loc_database_magic magic
;
663 make_magic(writer
, &magic
, version
);
666 struct loc_database_header_v1 header
;
667 header
.vendor
= htobe32(writer
->vendor
);
668 header
.description
= htobe32(writer
->description
);
669 header
.license
= htobe32(writer
->license
);
671 time_t now
= time(NULL
);
672 header
.created_at
= htobe64(now
);
674 // Clear the signatures
675 memset(header
.signature1
, '\0', sizeof(header
.signature1
));
676 header
.signature1_length
= 0;
677 memset(header
.signature2
, '\0', sizeof(header
.signature2
));
678 header
.signature2_length
= 0;
681 memset(header
.padding
, '\0', sizeof(header
.padding
));
686 // Start writing at the beginning of the file
687 r
= fseek(f
, 0, SEEK_SET
);
692 offset
+= fwrite(&magic
, 1, sizeof(magic
), f
);
694 // Skip the space we need to write the header later
695 r
= fseek(f
, sizeof(header
), SEEK_CUR
);
697 DEBUG(writer
->ctx
, "Could not seek to position after header\n");
700 offset
+= sizeof(header
);
702 align_page_boundary(&offset
, f
);
705 r
= loc_database_write_as_section(writer
, &header
, &offset
, f
);
709 // Write all networks
710 r
= loc_database_write_networks(writer
, &header
, &offset
, f
);
715 r
= loc_database_write_countries(writer
, &header
, &offset
, f
);
720 r
= loc_database_write_pool(writer
, &header
, &offset
, f
);
724 // Create the signatures
725 if (writer
->private_key1
) {
726 DEBUG(writer
->ctx
, "Creating signature with first private key\n");
728 writer
->signature1_length
= sizeof(writer
->signature1
);
730 r
= loc_writer_create_signature(writer
, &header
, f
,
731 writer
->private_key1
, writer
->signature1
, &writer
->signature1_length
);
736 if (writer
->private_key2
) {
737 DEBUG(writer
->ctx
, "Creating signature with second private key\n");
739 writer
->signature2_length
= sizeof(writer
->signature2
);
741 r
= loc_writer_create_signature(writer
, &header
, f
,
742 writer
->private_key2
, writer
->signature2
, &writer
->signature2_length
);
747 // Copy the signatures into the header
748 if (writer
->signature1_length
) {
749 DEBUG(writer
->ctx
, "Copying first signature of %zu byte(s)\n",
750 writer
->signature1_length
);
752 memcpy(header
.signature1
, writer
->signature1
, writer
->signature1_length
);
753 header
.signature1_length
= htobe16(writer
->signature1_length
);
756 if (writer
->signature2_length
) {
757 DEBUG(writer
->ctx
, "Copying second signature of %zu byte(s)\n",
758 writer
->signature2_length
);
760 memcpy(header
.signature2
, writer
->signature2
, writer
->signature2_length
);
761 header
.signature2_length
= htobe16(writer
->signature2_length
);
765 r
= fseek(f
, sizeof(magic
), SEEK_SET
);
769 fwrite(&header
, 1, sizeof(header
), f
);