]> git.ipfire.org Git - location/libloc.git/blobdiff - src/writer.c
location-importer.in: skip networks with unknown country codes
[location/libloc.git] / src / writer.c
index 6cd0027d2403bef8655616f440ae86613c770303..c61a6dfc657e92c1f552207f9af81cf671cb38da 100644 (file)
@@ -50,8 +50,15 @@ struct loc_writer {
        off_t description;
        off_t license;
 
-       // Private key to sign any databases
-       EVP_PKEY* private_key;
+       // Private keys to sign any databases
+       EVP_PKEY* private_key1;
+       EVP_PKEY* private_key2;
+
+       // Signatures
+       char signature1[LOC_SIGNATURE_MAX_LENGTH];
+       size_t signature1_length;
+       char signature2[LOC_SIGNATURE_MAX_LENGTH];
+       size_t signature2_length;
 
        struct loc_as** as;
        size_t as_count;
@@ -62,16 +69,16 @@ struct loc_writer {
        struct loc_network_tree* networks;
 };
 
-static int parse_private_key(struct loc_writer* writer, FILE* f) {
+static int parse_private_key(struct loc_writer* writer, EVP_PKEY** private_key, FILE* f) {
        // Free any previously loaded keys
-       if (writer->private_key)
-               EVP_PKEY_free(writer->private_key);
+       if (*private_key)
+               EVP_PKEY_free(*private_key);
 
        // Read the key
-       writer->private_key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
+       *private_key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
 
        // Log any errors
-       if (!writer->private_key) {
+       if (!*private_key) {
                char* error = ERR_error_string(ERR_get_error(), NULL);
                ERROR(writer->ctx, "Could not parse private key: %s\n", error);
 
@@ -81,7 +88,8 @@ static int parse_private_key(struct loc_writer* writer, FILE* f) {
        return 0;
 }
 
-LOC_EXPORT int loc_writer_new(struct loc_ctx* ctx, struct loc_writer** writer, FILE* fkey) {
+LOC_EXPORT int loc_writer_new(struct loc_ctx* ctx, struct loc_writer** writer,
+               FILE* fkey1, FILE* fkey2) {
        struct loc_writer* w = calloc(1, sizeof(*w));
        if (!w)
                return -ENOMEM;
@@ -102,9 +110,17 @@ LOC_EXPORT int loc_writer_new(struct loc_ctx* ctx, struct loc_writer** writer, F
                return r;
        }
 
-       // Load the private key to sign databases
-       if (fkey) {
-               r = parse_private_key(w, fkey);
+       // Load the private keys to sign databases
+       if (fkey1) {
+               r = parse_private_key(w, &w->private_key1, fkey1);
+               if (r) {
+                       loc_writer_unref(w);
+                       return r;
+               }
+       }
+
+       if (fkey2) {
+               r = parse_private_key(w, &w->private_key2, fkey2);
                if (r) {
                        loc_writer_unref(w);
                        return r;
@@ -124,13 +140,26 @@ LOC_EXPORT struct loc_writer* loc_writer_ref(struct loc_writer* writer) {
 static void loc_writer_free(struct loc_writer* writer) {
        DEBUG(writer->ctx, "Releasing writer at %p\n", writer);
 
-       // Free private key
-       if (writer->private_key)
-               EVP_PKEY_free(writer->private_key);
+       // Free private keys
+       if (writer->private_key1)
+               EVP_PKEY_free(writer->private_key1);
+       if (writer->private_key2)
+               EVP_PKEY_free(writer->private_key2);
 
        // Unref all AS
-       for (unsigned int i = 0; i < writer->as_count; i++) {
-               loc_as_unref(writer->as[i]);
+       if (writer->as) {
+               for (unsigned int i = 0; i < writer->as_count; i++) {
+                       loc_as_unref(writer->as[i]);
+               }
+               free(writer->as);
+       }
+
+       // Unref all countries
+       if (writer->countries) {
+               for (unsigned int i = 0; i < writer->countries_count; i++) {
+                       loc_country_unref(writer->countries[i]);
+               }
+               free(writer->countries);
        }
 
        // Release network tree
@@ -259,13 +288,14 @@ LOC_EXPORT int loc_writer_add_country(struct loc_writer* writer, struct loc_coun
        return 0;
 }
 
-static void make_magic(struct loc_writer* writer, struct loc_database_magic* magic) {
+static void make_magic(struct loc_writer* writer, struct loc_database_magic* magic,
+               enum loc_database_version version) {
        // Copy magic bytes
        for (unsigned int i = 0; i < strlen(LOC_DATABASE_MAGIC); i++)
                magic->magic[i] = LOC_DATABASE_MAGIC[i];
 
        // Set version
-       magic->version = htobe16(LOC_DATABASE_VERSION);
+       magic->version = version;
 }
 
 static void align_page_boundary(off_t* offset, FILE* f) {
@@ -275,7 +305,7 @@ static void align_page_boundary(off_t* offset, FILE* f) {
 }
 
 static int loc_database_write_pool(struct loc_writer* writer,
-               struct loc_database_header_v0* header, off_t* offset, FILE* f) {
+               struct loc_database_header_v1* header, off_t* offset, FILE* f) {
        // Save the offset of the pool section
        DEBUG(writer->ctx, "Pool starts at %jd bytes\n", (intmax_t)*offset);
        header->pool_offset = htobe32(*offset);
@@ -291,16 +321,16 @@ static int loc_database_write_pool(struct loc_writer* writer,
 }
 
 static int loc_database_write_as_section(struct loc_writer* writer,
-               struct loc_database_header_v0* header, off_t* offset, FILE* f) {
+               struct loc_database_header_v1* header, off_t* offset, FILE* f) {
        DEBUG(writer->ctx, "AS section starts at %jd bytes\n", (intmax_t)*offset);
        header->as_offset = htobe32(*offset);
 
        size_t as_length = 0;
 
-       struct loc_database_as_v0 as;
+       struct loc_database_as_v1 as;
        for (unsigned int i = 0; i < writer->as_count; i++) {
                // Convert AS into database format
-               loc_as_to_database_v0(writer->as[i], writer->pool, &as);
+               loc_as_to_database_v1(writer->as[i], writer->pool, &as);
 
                // Write to disk
                *offset += fwrite(&as, 1, sizeof(as), f);
@@ -365,7 +395,7 @@ static void free_network(struct network* network) {
 }
 
 static int loc_database_write_networks(struct loc_writer* writer,
-               struct loc_database_header_v0* header, off_t* offset, FILE* f) {
+               struct loc_database_header_v1* header, off_t* offset, FILE* f) {
        // Write the network tree
        DEBUG(writer->ctx, "Network tree starts at %jd bytes\n", (intmax_t)*offset);
        header->network_tree_offset = htobe32(*offset);
@@ -379,8 +409,8 @@ static int loc_database_write_networks(struct loc_writer* writer,
        uint32_t index = 0;
        uint32_t network_index = 0;
 
-       struct loc_database_network_v0 db_network;
-       struct loc_database_network_node_v0 db_node;
+       struct loc_database_network_v1 db_network;
+       struct loc_database_network_node_v1 db_node;
 
        // Initialize queue for nodes
        TAILQ_HEAD(node_t, node) nodes;
@@ -467,7 +497,7 @@ static int loc_database_write_networks(struct loc_writer* writer,
                TAILQ_REMOVE(&networks, nw, networks);
 
                // Prepare what we are writing to disk
-               int r = loc_network_to_database_v0(nw->network, &db_network);
+               int r = loc_network_to_database_v1(nw->network, &db_network);
                if (r)
                        return r;
 
@@ -485,16 +515,16 @@ static int loc_database_write_networks(struct loc_writer* writer,
 }
 
 static int loc_database_write_countries(struct loc_writer* writer,
-               struct loc_database_header_v0* header, off_t* offset, FILE* f) {
+               struct loc_database_header_v1* header, off_t* offset, FILE* f) {
        DEBUG(writer->ctx, "Countries section starts at %jd bytes\n", (intmax_t)*offset);
        header->countries_offset = htobe32(*offset);
 
        size_t countries_length = 0;
 
-       struct loc_database_country_v0 country;
+       struct loc_database_country_v1 country;
        for (unsigned int i = 0; i < writer->countries_count; i++) {
                // Convert country into database format
-               loc_country_to_database_v0(writer->countries[i], writer->pool, &country);
+               loc_country_to_database_v1(writer->countries[i], writer->pool, &country);
 
                // Write to disk
                *offset += fwrite(&country, 1, sizeof(country), f);
@@ -510,8 +540,9 @@ static int loc_database_write_countries(struct loc_writer* writer,
 }
 
 static int loc_writer_create_signature(struct loc_writer* writer,
-               struct loc_database_header_v0* header, FILE* f) {
-       DEBUG(writer->ctx, "Signing database...\n");
+               struct loc_database_header_v1* header, FILE* f, EVP_PKEY* private_key,
+               char* signature, size_t* length) {
+       DEBUG(writer->ctx, "Creating signature...\n");
 
        // Read file from the beginning
        rewind(f);
@@ -520,7 +551,7 @@ static int loc_writer_create_signature(struct loc_writer* writer,
        EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
 
        // Initialise the context
-       int r = EVP_DigestSignInit(mdctx, NULL, NULL, NULL, writer->private_key);
+       int r = EVP_DigestSignInit(mdctx, NULL, NULL, NULL, private_key);
        if (r != 1) {
                ERROR(writer->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
                goto END;
@@ -567,29 +598,25 @@ static int loc_writer_create_signature(struct loc_writer* writer,
                r = EVP_DigestSignUpdate(mdctx, buffer, bytes_read);
                if (r != 1) {
                        ERROR(writer->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+                       r = -1;
                        goto END;
                }
        }
 
        // Compute the signature
-       size_t signature_length = sizeof(header->signature);
-
        r = EVP_DigestSignFinal(mdctx,
-               (unsigned char*)header->signature, &signature_length);
+               (unsigned char*)signature, length);
        if (r != 1) {
                ERROR(writer->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+               r = -1;
                goto END;
        }
 
-       // Save length of the signature
-       header->signature_length = htobe32(signature_length);
-
-       DEBUG(writer->ctx, "Successfully generated signature of %lu bytes\n",
-               signature_length);
+       DEBUG(writer->ctx, "Successfully generated signature of %zu bytes\n", *length);
        r = 0;
 
        // Dump signature
-       hexdump(writer->ctx, header->signature, signature_length);
+       hexdump(writer->ctx, signature, *length);
 
 END:
        EVP_MD_CTX_free(mdctx);
@@ -597,12 +624,28 @@ END:
        return r;
 }
 
-LOC_EXPORT int loc_writer_write(struct loc_writer* writer, FILE* f) {
+LOC_EXPORT int loc_writer_write(struct loc_writer* writer, FILE* f, enum loc_database_version version) {
+       // Check version
+       switch (version) {
+               case LOC_DATABASE_VERSION_UNSET:
+                       version = LOC_DATABASE_VERSION_LATEST;
+                       break;
+
+               case LOC_DATABASE_VERSION_1:
+                       break;
+
+               default:
+                       ERROR(writer->ctx, "Invalid database version: %d\n", version);
+                       return -1;
+       }
+
+       DEBUG(writer->ctx, "Writing database in version %d\n", version);
+
        struct loc_database_magic magic;
-       make_magic(writer, &magic);
+       make_magic(writer, &magic, version);
 
        // Make the header
-       struct loc_database_header_v0 header;
+       struct loc_database_header_v1 header;
        header.vendor      = htobe32(writer->vendor);
        header.description = htobe32(writer->description);
        header.license     = htobe32(writer->license);
@@ -610,14 +653,14 @@ LOC_EXPORT int loc_writer_write(struct loc_writer* writer, FILE* f) {
        time_t now = time(NULL);
        header.created_at = htobe64(now);
 
-       // Clear the signature
-       header.signature_length = 0;
-       for (unsigned int i = 0; i < sizeof(header.signature); i++)
-               header.signature[i] = '\0';
+       // Clear the signatures
+       memset(header.signature1, '\0', sizeof(header.signature1));
+       header.signature1_length = 0;
+       memset(header.signature2, '\0', sizeof(header.signature2));
+       header.signature2_length = 0;
 
        // Clear the padding
-       for (unsigned int i = 0; i < sizeof(header.padding); i++)
-               header.padding[i] = '\0';
+       memset(header.padding, '\0', sizeof(header.padding));
 
        int r;
        off_t offset = 0;
@@ -660,13 +703,46 @@ LOC_EXPORT int loc_writer_write(struct loc_writer* writer, FILE* f) {
        if (r)
                return r;
 
-       // Create the signature
-       if (writer->private_key) {
-               r = loc_writer_create_signature(writer, &header, f);
+       // Create the signatures
+       if (writer->private_key1) {
+               DEBUG(writer->ctx, "Creating signature with first private key\n");
+
+               writer->signature1_length = sizeof(writer->signature1);
+
+               r = loc_writer_create_signature(writer, &header, f,
+                       writer->private_key1, writer->signature1, &writer->signature1_length);
+               if (r)
+                       return r;
+       }
+
+       if (writer->private_key2) {
+               DEBUG(writer->ctx, "Creating signature with second private key\n");
+
+               writer->signature2_length = sizeof(writer->signature2);
+
+               r = loc_writer_create_signature(writer, &header, f,
+                       writer->private_key2, writer->signature2, &writer->signature2_length);
                if (r)
                        return r;
        }
 
+       // Copy the signatures into the header
+       if (writer->signature1_length) {
+               DEBUG(writer->ctx, "Copying first signature of %zu byte(s)\n",
+                       writer->signature1_length);
+
+               memcpy(header.signature1, writer->signature1, writer->signature1_length);
+               header.signature1_length = htobe16(writer->signature1_length);
+       }
+
+       if (writer->signature2_length) {
+               DEBUG(writer->ctx, "Copying second signature of %zu byte(s)\n",
+                       writer->signature1_length);
+
+               memcpy(header.signature2, writer->signature2, writer->signature2_length);
+               header.signature2_length = htobe16(writer->signature2_length);
+       }
+
        // Write the header
        r = fseek(f, sizeof(magic), SEEK_SET);
        if (r)