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 <loc/libloc.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>
48 struct loc_stringpool
* pool
;
53 // Private key to sign any databases
54 EVP_PKEY
* private_key
;
59 struct loc_country
** countries
;
60 size_t countries_count
;
62 struct loc_network_tree
* networks
;
65 static int parse_private_key(struct loc_writer
* writer
, FILE* f
) {
66 // Free any previously loaded keys
67 if (writer
->private_key
)
68 EVP_PKEY_free(writer
->private_key
);
71 writer
->private_key
= PEM_read_PrivateKey(f
, NULL
, NULL
, NULL
);
74 if (!writer
->private_key
) {
75 char* error
= ERR_error_string(ERR_get_error(), NULL
);
76 ERROR(writer
->ctx
, "Could not parse private key: %s\n", error
);
84 LOC_EXPORT
int loc_writer_new(struct loc_ctx
* ctx
, struct loc_writer
** writer
, FILE* fkey
) {
85 struct loc_writer
* w
= calloc(1, sizeof(*w
));
89 w
->ctx
= loc_ref(ctx
);
92 int r
= loc_stringpool_new(ctx
, &w
->pool
);
98 // Initialize the network tree
99 r
= loc_network_tree_new(ctx
, &w
->networks
);
105 // Load the private key to sign databases
107 r
= parse_private_key(w
, fkey
);
118 LOC_EXPORT
struct loc_writer
* loc_writer_ref(struct loc_writer
* writer
) {
124 static void loc_writer_free(struct loc_writer
* writer
) {
125 DEBUG(writer
->ctx
, "Releasing writer at %p\n", writer
);
128 if (writer
->private_key
)
129 EVP_PKEY_free(writer
->private_key
);
132 for (unsigned int i
= 0; i
< writer
->as_count
; i
++) {
133 loc_as_unref(writer
->as
[i
]);
136 // Release network tree
137 if (writer
->networks
)
138 loc_network_tree_unref(writer
->networks
);
140 // Unref the string pool
141 loc_stringpool_unref(writer
->pool
);
143 loc_unref(writer
->ctx
);
147 LOC_EXPORT
struct loc_writer
* loc_writer_unref(struct loc_writer
* writer
) {
148 if (--writer
->refcount
> 0)
151 loc_writer_free(writer
);
156 LOC_EXPORT
const char* loc_writer_get_vendor(struct loc_writer
* writer
) {
157 return loc_stringpool_get(writer
->pool
, writer
->vendor
);
160 LOC_EXPORT
int loc_writer_set_vendor(struct loc_writer
* writer
, const char* vendor
) {
161 // Add the string to the string pool
162 off_t offset
= loc_stringpool_add(writer
->pool
, vendor
);
166 writer
->vendor
= offset
;
170 LOC_EXPORT
const char* loc_writer_get_description(struct loc_writer
* writer
) {
171 return loc_stringpool_get(writer
->pool
, writer
->description
);
174 LOC_EXPORT
int loc_writer_set_description(struct loc_writer
* writer
, const char* description
) {
175 // Add the string to the string pool
176 off_t offset
= loc_stringpool_add(writer
->pool
, description
);
180 writer
->description
= offset
;
184 LOC_EXPORT
const char* loc_writer_get_license(struct loc_writer
* writer
) {
185 return loc_stringpool_get(writer
->pool
, writer
->license
);
188 LOC_EXPORT
int loc_writer_set_license(struct loc_writer
* writer
, const char* license
) {
189 // Add the string to the string pool
190 off_t offset
= loc_stringpool_add(writer
->pool
, license
);
194 writer
->license
= offset
;
198 static int __loc_as_cmp(const void* as1
, const void* as2
) {
199 return loc_as_cmp(*(struct loc_as
**)as1
, *(struct loc_as
**)as2
);
202 LOC_EXPORT
int loc_writer_add_as(struct loc_writer
* writer
, struct loc_as
** as
, uint32_t number
) {
203 int r
= loc_as_new(writer
->ctx
, as
, number
);
207 // We have a new AS to add
211 writer
->as
= realloc(writer
->as
, sizeof(*writer
->as
) * writer
->as_count
);
215 // Add as last element
216 writer
->as
[writer
->as_count
- 1] = loc_as_ref(*as
);
219 qsort(writer
->as
, writer
->as_count
, sizeof(*writer
->as
), __loc_as_cmp
);
224 LOC_EXPORT
int loc_writer_add_network(struct loc_writer
* writer
, struct loc_network
** network
, const char* string
) {
227 // Create a new network object
228 r
= loc_network_new_from_string(writer
->ctx
, network
, string
);
232 // Add it to the local tree
233 return loc_network_tree_add_network(writer
->networks
, *network
);
236 static int __loc_country_cmp(const void* country1
, const void* country2
) {
237 return loc_country_cmp(*(struct loc_country
**)country1
, *(struct loc_country
**)country2
);
240 LOC_EXPORT
int loc_writer_add_country(struct loc_writer
* writer
, struct loc_country
** country
, const char* country_code
) {
241 int r
= loc_country_new(writer
->ctx
, country
, country_code
);
245 // We have a new country to add
246 writer
->countries_count
++;
249 writer
->countries
= realloc(writer
->countries
, sizeof(*writer
->countries
) * writer
->countries_count
);
250 if (!writer
->countries
)
253 // Add as last element
254 writer
->countries
[writer
->countries_count
- 1] = loc_country_ref(*country
);
257 qsort(writer
->countries
, writer
->countries_count
, sizeof(*writer
->countries
), __loc_country_cmp
);
262 static void make_magic(struct loc_writer
* writer
, struct loc_database_magic
* magic
,
263 enum loc_database_version version
) {
265 for (unsigned int i
= 0; i
< strlen(LOC_DATABASE_MAGIC
); i
++)
266 magic
->magic
[i
] = LOC_DATABASE_MAGIC
[i
];
269 magic
->version
= version
;
272 static void align_page_boundary(off_t
* offset
, FILE* f
) {
273 // Move to next page boundary
274 while (*offset
% LOC_DATABASE_PAGE_SIZE
> 0)
275 *offset
+= fwrite("", 1, 1, f
);
278 static int loc_database_write_pool(struct loc_writer
* writer
,
279 struct loc_database_header_v1
* header
, off_t
* offset
, FILE* f
) {
280 // Save the offset of the pool section
281 DEBUG(writer
->ctx
, "Pool starts at %jd bytes\n", (intmax_t)*offset
);
282 header
->pool_offset
= htobe32(*offset
);
285 size_t pool_length
= loc_stringpool_write(writer
->pool
, f
);
286 *offset
+= pool_length
;
288 DEBUG(writer
->ctx
, "Pool has a length of %zu bytes\n", pool_length
);
289 header
->pool_length
= htobe32(pool_length
);
294 static int loc_database_write_as_section(struct loc_writer
* writer
,
295 struct loc_database_header_v1
* header
, off_t
* offset
, FILE* f
) {
296 DEBUG(writer
->ctx
, "AS section starts at %jd bytes\n", (intmax_t)*offset
);
297 header
->as_offset
= htobe32(*offset
);
299 size_t as_length
= 0;
301 struct loc_database_as_v1 as
;
302 for (unsigned int i
= 0; i
< writer
->as_count
; i
++) {
303 // Convert AS into database format
304 loc_as_to_database_v1(writer
->as
[i
], writer
->pool
, &as
);
307 *offset
+= fwrite(&as
, 1, sizeof(as
), f
);
308 as_length
+= sizeof(as
);
311 DEBUG(writer
->ctx
, "AS section has a length of %zu bytes\n", as_length
);
312 header
->as_length
= htobe32(as_length
);
314 align_page_boundary(offset
, f
);
320 TAILQ_ENTRY(node
) nodes
;
322 struct loc_network_tree_node
* node
;
324 // Indices of the child nodes
329 static struct node
* make_node(struct loc_network_tree_node
* node
) {
330 struct node
* n
= malloc(sizeof(*n
));
334 n
->node
= loc_network_tree_node_ref(node
);
335 n
->index_zero
= n
->index_one
= 0;
340 static void free_node(struct node
* node
) {
341 loc_network_tree_node_unref(node
->node
);
347 TAILQ_ENTRY(network
) networks
;
349 struct loc_network
* network
;
352 static struct network
* make_network(struct loc_network
* network
) {
353 struct network
* n
= malloc(sizeof(*n
));
357 n
->network
= loc_network_ref(network
);
362 static void free_network(struct network
* network
) {
363 loc_network_unref(network
->network
);
368 static int loc_database_write_networks(struct loc_writer
* writer
,
369 struct loc_database_header_v1
* header
, off_t
* offset
, FILE* f
) {
370 // Write the network tree
371 DEBUG(writer
->ctx
, "Network tree starts at %jd bytes\n", (intmax_t)*offset
);
372 header
->network_tree_offset
= htobe32(*offset
);
374 size_t network_tree_length
= 0;
375 size_t network_data_length
= 0;
378 struct node
* child_node
;
381 uint32_t network_index
= 0;
383 struct loc_database_network_v1 db_network
;
384 struct loc_database_network_node_v1 db_node
;
386 // Initialize queue for nodes
387 TAILQ_HEAD(node_t
, node
) nodes
;
390 // Initialize queue for networks
391 TAILQ_HEAD(network_t
, network
) networks
;
392 TAILQ_INIT(&networks
);
395 struct loc_network_tree_node
* root
= loc_network_tree_get_root(writer
->networks
);
396 node
= make_node(root
);
398 TAILQ_INSERT_TAIL(&nodes
, node
, nodes
);
400 while (!TAILQ_EMPTY(&nodes
)) {
401 // Pop first node in list
402 node
= TAILQ_FIRST(&nodes
);
403 TAILQ_REMOVE(&nodes
, node
, nodes
);
405 DEBUG(writer
->ctx
, "Processing node %p\n", node
);
408 struct loc_network_tree_node
* node_zero
= loc_network_tree_node_get(node
->node
, 0);
410 node
->index_zero
= ++index
;
412 child_node
= make_node(node_zero
);
413 loc_network_tree_node_unref(node_zero
);
415 TAILQ_INSERT_TAIL(&nodes
, child_node
, nodes
);
418 struct loc_network_tree_node
* node_one
= loc_network_tree_node_get(node
->node
, 1);
420 node
->index_one
= ++index
;
422 child_node
= make_node(node_one
);
423 loc_network_tree_node_unref(node_one
);
425 TAILQ_INSERT_TAIL(&nodes
, child_node
, nodes
);
428 // Prepare what we are writing to disk
429 db_node
.zero
= htobe32(node
->index_zero
);
430 db_node
.one
= htobe32(node
->index_one
);
432 if (loc_network_tree_node_is_leaf(node
->node
)) {
433 struct loc_network
* network
= loc_network_tree_node_get_network(node
->node
);
435 // Append network to be written out later
436 struct network
* nw
= make_network(network
);
437 TAILQ_INSERT_TAIL(&networks
, nw
, networks
);
439 db_node
.network
= htobe32(network_index
++);
440 loc_network_unref(network
);
442 db_node
.network
= htobe32(0xffffffff);
445 // Write the current node
446 DEBUG(writer
->ctx
, "Writing node %p (0 = %d, 1 = %d)\n",
447 node
, node
->index_zero
, node
->index_one
);
449 *offset
+= fwrite(&db_node
, 1, sizeof(db_node
), f
);
450 network_tree_length
+= sizeof(db_node
);
455 loc_network_tree_node_unref(root
);
457 header
->network_tree_length
= htobe32(network_tree_length
);
459 align_page_boundary(offset
, f
);
461 DEBUG(writer
->ctx
, "Networks data section starts at %jd bytes\n", (intmax_t)*offset
);
462 header
->network_data_offset
= htobe32(*offset
);
464 // We have now written the entire tree and have all networks
465 // in a queue in order as they are indexed
466 while (!TAILQ_EMPTY(&networks
)) {
467 struct network
* nw
= TAILQ_FIRST(&networks
);
468 TAILQ_REMOVE(&networks
, nw
, networks
);
470 // Prepare what we are writing to disk
471 int r
= loc_network_to_database_v1(nw
->network
, &db_network
);
475 *offset
+= fwrite(&db_network
, 1, sizeof(db_network
), f
);
476 network_data_length
+= sizeof(db_network
);
481 header
->network_data_length
= htobe32(network_data_length
);
483 align_page_boundary(offset
, f
);
488 static int loc_database_write_countries(struct loc_writer
* writer
,
489 struct loc_database_header_v1
* header
, off_t
* offset
, FILE* f
) {
490 DEBUG(writer
->ctx
, "Countries section starts at %jd bytes\n", (intmax_t)*offset
);
491 header
->countries_offset
= htobe32(*offset
);
493 size_t countries_length
= 0;
495 struct loc_database_country_v1 country
;
496 for (unsigned int i
= 0; i
< writer
->countries_count
; i
++) {
497 // Convert country into database format
498 loc_country_to_database_v1(writer
->countries
[i
], writer
->pool
, &country
);
501 *offset
+= fwrite(&country
, 1, sizeof(country
), f
);
502 countries_length
+= sizeof(country
);
505 DEBUG(writer
->ctx
, "Countries section has a length of %zu bytes\n", countries_length
);
506 header
->countries_length
= htobe32(countries_length
);
508 align_page_boundary(offset
, f
);
513 static int loc_writer_create_signature(struct loc_writer
* writer
,
514 struct loc_database_header_v1
* header
, FILE* f
) {
515 DEBUG(writer
->ctx
, "Signing database...\n");
517 // Read file from the beginning
520 // Create a new context for signing
521 EVP_MD_CTX
* mdctx
= EVP_MD_CTX_new();
523 // Initialise the context
524 int r
= EVP_DigestSignInit(mdctx
, NULL
, NULL
, NULL
, writer
->private_key
);
526 ERROR(writer
->ctx
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
531 struct loc_database_magic magic
;
532 fread(&magic
, 1, sizeof(magic
), f
);
534 hexdump(writer
->ctx
, &magic
, sizeof(magic
));
536 // Feed magic into the signature
537 r
= EVP_DigestSignUpdate(mdctx
, &magic
, sizeof(magic
));
539 ERROR(writer
->ctx
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
543 hexdump(writer
->ctx
, header
, sizeof(*header
));
545 // Feed the header into the signature
546 r
= EVP_DigestSignUpdate(mdctx
, header
, sizeof(*header
));
548 ERROR(writer
->ctx
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
553 fseek(f
, sizeof(*header
), SEEK_CUR
);
555 // Walk through the file in chunks of 64kB
556 char buffer
[64 * 1024];
558 size_t bytes_read
= fread(buffer
, 1, sizeof(buffer
), f
);
561 ERROR(writer
->ctx
, "Error reading from file: %s\n", strerror(errno
));
566 hexdump(writer
->ctx
, buffer
, bytes_read
);
568 r
= EVP_DigestSignUpdate(mdctx
, buffer
, bytes_read
);
570 ERROR(writer
->ctx
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
575 // Compute the signature
576 size_t signature_length
= sizeof(header
->signature
);
578 r
= EVP_DigestSignFinal(mdctx
,
579 (unsigned char*)header
->signature
, &signature_length
);
581 ERROR(writer
->ctx
, "%s\n", ERR_error_string(ERR_get_error(), NULL
));
585 // Save length of the signature
586 header
->signature_length
= htobe32(signature_length
);
588 DEBUG(writer
->ctx
, "Successfully generated signature of %lu bytes\n",
593 hexdump(writer
->ctx
, header
->signature
, signature_length
);
596 EVP_MD_CTX_free(mdctx
);
601 LOC_EXPORT
int loc_writer_write(struct loc_writer
* writer
, FILE* f
, enum loc_database_version version
) {
604 case LOC_DATABASE_VERSION_UNSET
:
605 version
= LOC_DATABASE_VERSION_LATEST
;
608 case LOC_DATABASE_VERSION_1
:
612 ERROR(writer
->ctx
, "Invalid database version: %d\n", version
);
616 DEBUG(writer
->ctx
, "Writing database in version %d\n", version
);
618 struct loc_database_magic magic
;
619 make_magic(writer
, &magic
, version
);
622 struct loc_database_header_v1 header
;
623 header
.vendor
= htobe32(writer
->vendor
);
624 header
.description
= htobe32(writer
->description
);
625 header
.license
= htobe32(writer
->license
);
627 time_t now
= time(NULL
);
628 header
.created_at
= htobe64(now
);
630 // Clear the signature
631 header
.signature_length
= 0;
632 for (unsigned int i
= 0; i
< sizeof(header
.signature
); i
++)
633 header
.signature
[i
] = '\0';
636 for (unsigned int i
= 0; i
< sizeof(header
.padding
); i
++)
637 header
.padding
[i
] = '\0';
642 // Start writing at the beginning of the file
643 r
= fseek(f
, 0, SEEK_SET
);
648 offset
+= fwrite(&magic
, 1, sizeof(magic
), f
);
650 // Skip the space we need to write the header later
651 r
= fseek(f
, sizeof(header
), SEEK_CUR
);
653 DEBUG(writer
->ctx
, "Could not seek to position after header\n");
656 offset
+= sizeof(header
);
658 align_page_boundary(&offset
, f
);
661 r
= loc_database_write_as_section(writer
, &header
, &offset
, f
);
665 // Write all networks
666 r
= loc_database_write_networks(writer
, &header
, &offset
, f
);
671 r
= loc_database_write_countries(writer
, &header
, &offset
, f
);
676 r
= loc_database_write_pool(writer
, &header
, &offset
, f
);
680 // Create the signature
681 if (writer
->private_key
) {
682 r
= loc_writer_create_signature(writer
, &header
, f
);
688 r
= fseek(f
, sizeof(magic
), SEEK_SET
);
692 fwrite(&header
, 1, sizeof(header
), f
);