pkginclude_HEADERS = \
src/loc/libloc.h \
src/loc/as.h \
+ src/loc/country.h \
src/loc/database.h \
src/loc/format.h \
src/loc/network.h \
src_libloc_la_SOURCES = \
src/libloc.c \
src/as.c \
+ src/country.c \
src/database.c \
src/network.c \
src/stringpool.c \
src/test-stringpool \
src/test-database \
src/test-as \
- src/test-network
+ src/test-network \
+ src/test-country
CLEANFILES += \
test.db \
src/test-stringpool \
src/test-database \
src/test-as \
- src/test-network
+ src/test-network \
+ src/test-country
src_test_libloc_SOURCES = \
src/test-libloc.c
src_test_as_LDADD = \
src/libloc.la
+src_test_country_SOURCES = \
+ src/test-country.c
+
+src_test_country_CFLAGS = \
+ $(TESTS_CFLAGS)
+
+src_test_country_LDADD = \
+ src/libloc.la
+
src_test_network_SOURCES = \
src/test-network.c
test-as
test-libloc
test-database
+test-country
test-network
test-stringpool
--- /dev/null
+/*
+ libloc - A library to determine the location of someone on the Internet
+
+ Copyright (C) 2019 IPFire Development Team <info@ipfire.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+*/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <loc/libloc.h>
+#include <loc/country.h>
+#include <loc/private.h>
+
+struct loc_country {
+ struct loc_ctx* ctx;
+ int refcount;
+
+ char* code;
+ char* continent_code;
+
+ char* name;
+};
+
+LOC_EXPORT int loc_country_new(struct loc_ctx* ctx, struct loc_country** country, const char* country_code) {
+ struct loc_country* c = calloc(1, sizeof(*c));
+ if (!c)
+ return -ENOMEM;
+
+ c->ctx = loc_ref(ctx);
+ c->refcount = 1;
+
+ c->code = strdup(country_code);
+
+ DEBUG(c->ctx, "Country %s allocated at %p\n", c->code, c);
+ *country = c;
+
+ return 0;
+}
+
+LOC_EXPORT struct loc_country* loc_country_ref(struct loc_country* country) {
+ country->refcount++;
+
+ return country;
+}
+
+static void loc_country_free(struct loc_country* country) {
+ DEBUG(country->ctx, "Releasing country %s %p\n", country->code, country);
+
+ if (country->code)
+ free(country->code);
+
+ if (country->continent_code)
+ free(country->continent_code);
+
+ if (country->name)
+ free(country->name);
+
+ loc_unref(country->ctx);
+ free(country);
+}
+
+LOC_EXPORT struct loc_country* loc_country_unref(struct loc_country* country) {
+ if (--country->refcount > 0)
+ return NULL;
+
+ loc_country_free(country);
+
+ return NULL;
+}
+
+LOC_EXPORT const char* loc_country_get_code(struct loc_country* country) {
+ return country->code;
+}
+
+LOC_EXPORT const char* loc_country_get_continent_code(struct loc_country* country) {
+ return country->continent_code;
+}
+
+LOC_EXPORT int loc_country_set_continent_code(struct loc_country* country, const char* continent_code) {
+ // XXX validate input
+
+ // Free previous value
+ if (country->continent_code)
+ free(country->continent_code);
+
+ country->continent_code = strdup(continent_code);
+
+ return 0;
+}
+
+LOC_EXPORT const char* loc_country_get_name(struct loc_country* country) {
+ return country->name;
+}
+
+LOC_EXPORT int loc_country_set_name(struct loc_country* country, const char* name) {
+ if (country->name)
+ free(country->name);
+
+ if (name)
+ country->name = strdup(name);
+
+ return 0;
+}
+
+int loc_country_cmp(struct loc_country* country1, struct loc_country* country2) {
+ return strcmp(country1->code, country2->code);
+}
+
+int loc_country_new_from_database_v0(struct loc_ctx* ctx, struct loc_stringpool* pool,
+ struct loc_country** country, const struct loc_database_country_v0* dbobj) {
+ char buffer[3];
+
+ // Read country code
+ loc_country_copy_code(buffer, dbobj->code);
+
+ // Create a new country object
+ int r = loc_country_new(ctx, country, buffer);
+ if (r)
+ return r;
+
+ // Continent Code
+ loc_country_copy_code(buffer, dbobj->continent_code);
+
+ r = loc_country_set_continent_code(*country, buffer);
+ if (r)
+ goto FAIL;
+
+ // Set name
+#if 0
+ // XXX Reading from the stringpool makes test-country.c fail
+ const char* name = loc_stringpool_get(pool, be32toh(dbobj->name));
+ if (name) {
+ r = loc_country_set_name(*country, name);
+ if (r)
+ goto FAIL;
+ }
+#endif
+
+ return 0;
+
+FAIL:
+ loc_country_unref(*country);
+ return r;
+}
+
+int loc_country_to_database_v0(struct loc_country* country,
+ struct loc_stringpool* pool, struct loc_database_country_v0* dbobj) {
+ // Add country code
+ for (unsigned int i = 0; i < 2; i++) {
+ dbobj->code[i] = country->code[i] ? country->code[i] : '\0';
+ }
+
+ // Add continent code
+ for (unsigned int i = 0; i < 2; i++) {
+ dbobj->continent_code[i] = country->continent_code[i] ? country->continent_code[i] : '\0';
+ }
+
+ // Save the name string in the string pool
+ off_t name = loc_stringpool_add(pool, country->name ? country->name : "");
+ dbobj->name = htobe32(name);
+
+ return 0;
+}
#include <loc/libloc.h>
#include <loc/as.h>
+#include <loc/country.h>
#include <loc/database.h>
#include <loc/format.h>
#include <loc/network.h>
struct loc_database_network_v0* networks_v0;
size_t networks_count;
+ // Countries
+ struct loc_database_country_v0* countries_v0;
+ size_t countries_count;
+
struct loc_stringpool* pool;
};
return 0;
}
+static int loc_database_read_countries_section_v0(struct loc_database* db,
+ FILE* f, const struct loc_database_header_v0* header) {
+ off_t countries_offset = be32toh(header->countries_offset);
+ size_t countries_length = be32toh(header->countries_length);
+
+ DEBUG(db->ctx, "Reading countries section from %jd (%zu bytes)\n",
+ countries_offset, countries_length);
+
+ if (countries_length > 0) {
+ db->countries_v0 = mmap(NULL, countries_length, PROT_READ,
+ MAP_SHARED, fileno(f), countries_offset);
+
+ if (db->countries_v0 == MAP_FAILED)
+ return -errno;
+ }
+
+ db->countries_count = countries_length / sizeof(*db->countries_v0);
+
+ INFO(db->ctx, "Read %zu countries from the database\n",
+ db->countries_count);
+
+ return 0;
+}
+
static int loc_database_read_header_v0(struct loc_database* db, FILE* f) {
struct loc_database_header_v0 header;
if (r)
return r;
+ // countries
+ r = loc_database_read_countries_section_v0(db, f, &header);
+ if (r)
+ return r;
+
return 0;
}
return loc_database_lookup(db, &address, network);
}
+// Returns the country at position pos
+static int loc_database_fetch_country(struct loc_database* db,
+ struct loc_country** country, off_t pos) {
+ if ((size_t)pos >= db->countries_count)
+ return -EINVAL;
+
+ DEBUG(db->ctx, "Fetching country at position %jd\n", pos);
+
+ int r;
+ switch (db->version) {
+ case 0:
+ r = loc_country_new_from_database_v0(db->ctx, db->pool, country, db->countries_v0 + pos);
+ break;
+
+ default:
+ return -1;
+ }
+
+ if (r == 0) {
+ DEBUG(db->ctx, "Got country %s\n", loc_country_get_code(*country));
+ }
+
+ return r;
+}
+
+// Performs a binary search to find the country in the list
+LOC_EXPORT int loc_database_get_country(struct loc_database* db,
+ struct loc_country** country, const char* code) {
+ off_t lo = 0;
+ off_t hi = db->countries_count - 1;
+
+ // Save start time
+ clock_t start = clock();
+
+ while (lo <= hi) {
+ off_t i = (lo + hi) / 2;
+
+ // Fetch country in the middle between lo and hi
+ int r = loc_database_fetch_country(db, country, i);
+ if (r)
+ return r;
+
+ // Check if this is a match
+ const char* cc = loc_country_get_code(*country);
+ int result = strcmp(code, cc);
+
+ if (result == 0) {
+ clock_t end = clock();
+
+ // Log how fast this has been
+ DEBUG(db->ctx, "Found country %s in %.4fms\n", cc,
+ (double)(end - start) / CLOCKS_PER_SEC * 1000);
+
+ return 0;
+ }
+
+ // If it wasn't, we release the country and
+ // adjust our search pointers
+ loc_country_unref(*country);
+
+ if (result < 0) {
+ lo = i + 1;
+ } else
+ hi = i - 1;
+ }
+
+ // Nothing found
+ *country = NULL;
+
+ return 1;
+}
+
// Enumerator
LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
loc_as_set_name;
loc_as_unref;
+ # Country
+ loc_country_get_code;
+ loc_country_get_continent_code;
+ loc_country_get_name;
+ loc_country_new;
+ loc_country_ref;
+ loc_country_set_continent_code;
+ loc_country_set_name;
+ loc_country_unref;
+
# Database
loc_database_add_as;
loc_database_count_as;
loc_database_created_at;
loc_database_get_as;
+ loc_database_get_country;
loc_database_get_description;
loc_database_get_license;
loc_database_get_vendor;
# Writer
loc_writer_add_as;
+ loc_writer_add_country;
loc_writer_add_network;
loc_writer_get_description;
loc_writer_get_license;
--- /dev/null
+/*
+ libloc - A library to determine the location of someone on the Internet
+
+ Copyright (C) 2019 IPFire Development Team <info@ipfire.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+*/
+
+#ifndef LIBLOC_COUNTRY_H
+#define LIBLOC_COUNTRY_H
+
+#include <loc/libloc.h>
+#include <loc/format.h>
+#include <loc/stringpool.h>
+
+struct loc_country;
+int loc_country_new(struct loc_ctx* ctx, struct loc_country** country, const char* country_code);
+struct loc_country* loc_country_ref(struct loc_country* country);
+struct loc_country* loc_country_unref(struct loc_country* country);
+
+const char* loc_country_get_code(struct loc_country* country);
+
+const char* loc_country_get_continent_code(struct loc_country* country);
+int loc_country_set_continent_code(struct loc_country* country, const char* continent_code);
+
+const char* loc_country_get_name(struct loc_country* country);
+int loc_country_set_name(struct loc_country* country, const char* name);
+
+int loc_country_cmp(struct loc_country* country1, struct loc_country* country2);
+
+#ifdef LIBLOC_PRIVATE
+
+int loc_country_new_from_database_v0(struct loc_ctx* ctx, struct loc_stringpool* pool,
+ struct loc_country** country, const struct loc_database_country_v0* dbobj);
+int loc_country_to_database_v0(struct loc_country* country,
+ struct loc_stringpool* pool, struct loc_database_country_v0* dbobj);
+
+static inline void loc_country_copy_code(char* dst, const char* src) {
+ for (unsigned int i = 0; i < 2; i++) {
+ dst[i] = src[i];
+ }
+
+ // Terminate the string
+ dst[3] = '\0';
+}
+
+#endif
+
+#endif
#include <loc/libloc.h>
#include <loc/network.h>
#include <loc/as.h>
+#include <loc/country.h>
struct loc_database;
int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE* f);
int loc_database_lookup_from_string(struct loc_database* db,
const char* string, struct loc_network** network);
+int loc_database_get_country(struct loc_database* db,
+ struct loc_country** country, const char* code);
+
enum loc_database_enumerator_mode {
LOC_DB_ENUMERATE_NETWORKS = 1,
LOC_DB_ENUMERATE_ASES = 2,
uint32_t network_tree_offset;
uint32_t network_tree_length;
+ // Tells us where the countries start
+ uint32_t countries_offset;
+ uint32_t countries_length;
+
// Tells us where the pool starts
uint32_t pool_offset;
uint32_t pool_length;
uint32_t name;
};
+struct loc_database_country_v0 {
+ char code[2];
+ char continent_code[2];
+
+ // Name in the string pool
+ uint32_t name;
+};
+
#endif
#endif
#include <loc/libloc.h>
#include <loc/as.h>
+#include <loc/country.h>
#include <loc/network.h>
struct loc_writer;
int loc_writer_add_as(struct loc_writer* writer, struct loc_as** as, uint32_t number);
int loc_writer_add_network(struct loc_writer* writer, struct loc_network** network, const char* string);
+int loc_writer_add_country(struct loc_writer* writer, struct loc_country** country, const char* country_code);
int loc_writer_write(struct loc_writer* writer, FILE* f);
--- /dev/null
+/*
+ libloc - A library to determine the location of someone on the Internet
+
+ Copyright (C) 2019 IPFire Development Team <info@ipfire.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <loc/libloc.h>
+#include <loc/database.h>
+#include <loc/network.h>
+#include <loc/writer.h>
+
+int main(int argc, char** argv) {
+ struct loc_country* country;
+ int err;
+
+ struct loc_ctx* ctx;
+ err = loc_new(&ctx);
+ if (err < 0)
+ exit(EXIT_FAILURE);
+
+ // Create a database
+ struct loc_writer* writer;
+ err = loc_writer_new(ctx, &writer);
+ if (err < 0)
+ exit(EXIT_FAILURE);
+
+ // Create a country
+ err = loc_writer_add_country(writer, &country, "T1");
+ if (err) {
+ fprintf(stderr, "Could not create country\n");
+ exit(EXIT_FAILURE);
+ }
+
+ // Set name & continent
+ loc_country_set_name(country, "Testistan");
+ loc_country_set_continent_code(country, "XX");
+
+ // Free country
+ loc_country_unref(country);
+
+ FILE* f = fopen("test.db", "w");
+ if (!f) {
+ fprintf(stderr, "Could not open file for writing: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ err = loc_writer_write(writer, f);
+ if (err) {
+ fprintf(stderr, "Could not write database: %s\n", strerror(-err));
+ exit(EXIT_FAILURE);
+ }
+ fclose(f);
+
+ loc_writer_unref(writer);
+
+ // And open it again from disk
+ f = fopen("test.db", "r");
+ if (!f) {
+ fprintf(stderr, "Could not open file for reading: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ struct loc_database* db;
+ err = loc_database_new(ctx, &db, f);
+ if (err) {
+ fprintf(stderr, "Could not open database: %s\n", strerror(-err));
+ exit(EXIT_FAILURE);
+ }
+
+ // Lookup an address in the subnet
+ err = loc_database_get_country(db, &country, "T1");
+ if (err) {
+ fprintf(stderr, "Could not find country T1\n");
+ exit(EXIT_FAILURE);
+ }
+ loc_country_unref(country);
+
+ loc_database_unref(db);
+ loc_unref(ctx);
+
+ return EXIT_SUCCESS;
+}
if (err < 0)
exit(EXIT_FAILURE);
+ // Try reading some invalid string
+ const char* s = loc_stringpool_get(pool, 100);
+ if (s != NULL) {
+ fprintf(stderr, "An unexpected string was returned: %s\n", s);
+ exit(EXIT_FAILURE);
+ }
+
// Append a string
off_t pos = loc_stringpool_add(pool, "ABC");
if (pos < 0) {
#include <loc/libloc.h>
#include <loc/as.h>
+#include <loc/country.h>
#include <loc/format.h>
#include <loc/network.h>
#include <loc/private.h>
struct loc_as** as;
size_t as_count;
+ struct loc_country** countries;
+ size_t countries_count;
+
struct loc_network_tree* networks;
};
return loc_network_tree_add_network(writer->networks, *network);
}
+static int __loc_country_cmp(const void* country1, const void* country2) {
+ return loc_country_cmp(*(struct loc_country**)country1, *(struct loc_country**)country2);
+}
+
+LOC_EXPORT int loc_writer_add_country(struct loc_writer* writer, struct loc_country** country, const char* country_code) {
+ int r = loc_country_new(writer->ctx, country, country_code);
+ if (r)
+ return r;
+
+ // We have a new country to add
+ writer->countries_count++;
+
+ // Make space
+ writer->countries = realloc(writer->countries, sizeof(*writer->countries) * writer->countries_count);
+ if (!writer->countries)
+ return -ENOMEM;
+
+ // Add as last element
+ writer->countries[writer->countries_count - 1] = loc_country_ref(*country);
+
+ // Sort everything
+ qsort(writer->countries, writer->countries_count, sizeof(*writer->countries), __loc_country_cmp);
+
+ return 0;
+}
+
static void make_magic(struct loc_writer* writer, struct loc_database_magic* magic) {
// Copy magic bytes
for (unsigned int i = 0; i < strlen(LOC_DATABASE_MAGIC); i++)
return 0;
}
+static int loc_database_write_countries(struct loc_writer* writer,
+ struct loc_database_header_v0* header, off_t* offset, FILE* f) {
+ DEBUG(writer->ctx, "Countries section starts at %jd bytes\n", *offset);
+ header->countries_offset = htobe32(*offset);
+
+ size_t countries_length = 0;
+
+ struct loc_database_country_v0 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);
+
+ // Write to disk
+ *offset += fwrite(&country, 1, sizeof(country), f);
+ countries_length += sizeof(country);
+ }
+
+ DEBUG(writer->ctx, "Countries section has a length of %zu bytes\n", countries_length);
+ header->countries_length = htobe32(countries_length);
+
+ align_page_boundary(offset, f);
+
+ return 0;
+}
+
LOC_EXPORT int loc_writer_write(struct loc_writer* writer, FILE* f) {
struct loc_database_magic magic;
make_magic(writer, &magic);
if (r)
return r;
+ // Write countries
+ r = loc_database_write_countries(writer, &header, &offset, f);
+ if (r)
+ return r;
+
// Write the header
r = fseek(f, sizeof(magic), SEEK_SET);
if (r)