From a5db3e497572e59767a44a254b75c2a99aebb438 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Tue, 12 Dec 2017 11:40:47 +0000 Subject: [PATCH] Introduce object to store an AS Signed-off-by: Michael Tremer --- Makefile.am | 15 +++++- configure.ac | 3 +- src/.gitignore | 1 + src/as.c | 126 +++++++++++++++++++++++++++++++++++++++++++ src/as.h | 43 +++++++++++++++ src/database.c | 137 ++++++++++++++++++++++++++++++++++++++++++----- src/database.h | 6 +++ src/libloc.sym | 10 ++++ src/loc/format.h | 46 ++++++++++++++++ src/stringpool.h | 1 + src/test-as.c | 88 ++++++++++++++++++++++++++++++ 11 files changed, 461 insertions(+), 15 deletions(-) create mode 100644 src/as.c create mode 100644 src/as.h create mode 100644 src/loc/format.h create mode 100644 src/test-as.c diff --git a/Makefile.am b/Makefile.am index a47cdae..6f4875d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -36,6 +36,7 @@ pkgconfigdir = $(libdir)/pkgconfig $(SED_PROCESS) pkginclude_HEADERS = \ + src/loc/format.h \ src/loc/libloc.h lib_LTLIBRARIES = \ @@ -44,6 +45,8 @@ lib_LTLIBRARIES = \ src_libloc_la_SOURCES =\ src/libloc-private.h \ src/libloc.c \ + src/as.c \ + src/as.h \ src/database.c \ src/database.h \ src/stringpool.c \ @@ -71,12 +74,14 @@ CLEANFILES += \ TESTS = \ src/test-libloc \ src/test-stringpool \ - src/test-database + src/test-database \ + src/test-as check_PROGRAMS = \ src/test-libloc \ src/test-stringpool \ - src/test-database + src/test-database \ + src/test-as src_test_libloc_SOURCES = \ src/test-libloc.c @@ -84,6 +89,12 @@ src_test_libloc_SOURCES = \ src_test_libloc_LDADD = \ src/libloc.la +src_test_as_SOURCES = \ + src/test-as.c + +src_test_as_LDADD = \ + src/libloc.la + src_test_stringpool_SOURCES = \ src/test-stringpool.c diff --git a/configure.ac b/configure.ac index 0c9b22a..ace67d3 100644 --- a/configure.ac +++ b/configure.ac @@ -41,7 +41,8 @@ AS_IF([test "x$enable_debug" = "xyes"], [ AC_CHECK_FUNCS([ \ __secure_getenv \ - secure_getenv\ + secure_getenv \ + qsort \ ]) my_CFLAGS="\ diff --git a/src/.gitignore b/src/.gitignore index 15e9181..8c2a12c 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -5,6 +5,7 @@ *.lo *.trs libloc.pc +test-as test-libloc test-database test-stringpool diff --git a/src/as.c b/src/as.c new file mode 100644 index 0000000..654ab26 --- /dev/null +++ b/src/as.c @@ -0,0 +1,126 @@ +/* + libloc - A library to determine the location of someone on the Internet + + Copyright (C) 2017 IPFire Development Team + + 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 +#include +#include +#include + +#include +#include + +#include "libloc-private.h" +#include "as.h" +#include "stringpool.h" + +struct loc_as { + struct loc_ctx* ctx; + int refcount; + + struct loc_stringpool* pool; + + uint32_t number; + off_t name; +}; + +LOC_EXPORT int loc_as_new(struct loc_ctx* ctx, struct loc_stringpool* pool, struct loc_as** as, uint32_t number) { + struct loc_as* a = calloc(1, sizeof(*a)); + if (!a) + return -ENOMEM; + + a->ctx = loc_ref(ctx); + a->refcount = 1; + a->pool = loc_stringpool_ref(pool); + + a->number = number; + + DEBUG(a->ctx, "AS%u allocated at %p\n", a->number, a); + *as = a; + + return 0; +} + +LOC_EXPORT struct loc_as* loc_as_ref(struct loc_as* as) { + as->refcount++; + + return as; +} + +static void loc_as_free(struct loc_as* as) { + DEBUG(as->ctx, "Releasing AS%u %p\n", as->number, as); + + loc_stringpool_unref(as->pool); + loc_unref(as->ctx); + + free(as); +} + +LOC_EXPORT struct loc_as* loc_as_unref(struct loc_as* as) { + if (--as->refcount > 0) + return NULL; + + loc_as_free(as); + + return NULL; +} + +LOC_EXPORT uint32_t loc_as_get_number(struct loc_as* as) { + return as->number; +} + +LOC_EXPORT const char* loc_as_get_name(struct loc_as* as) { + return loc_stringpool_get(as->pool, as->name); +} + +LOC_EXPORT int loc_as_set_name(struct loc_as* as, const char* name) { + // Add the string to the string pool + off_t offset = loc_stringpool_add(as->pool, name); + if (offset < 0) + return offset; + + as->name = offset; + return 0; +} + +LOC_EXPORT int loc_as_cmp(struct loc_as* as1, struct loc_as* as2) { + if (as1->number > as2->number) + return 1; + + if (as1->number < as2->number) + return -1; + + return 0; +} + +int loc_as_new_from_database_v0(struct loc_ctx* ctx, struct loc_stringpool* pool, + struct loc_as** as, const struct loc_database_as_v0* dbobj) { + uint32_t number = ntohl(dbobj->number); + + int r = loc_as_new(ctx, pool, as, number); + if (r) + return r; + + (*as)->name = ntohl(dbobj->name); + + return 0; +} + +int loc_as_to_database_v0(struct loc_as* as, struct loc_database_as_v0* dbobj) { + dbobj->number = htonl(as->number); + dbobj->name = htonl(as->name); + + return 0; +} diff --git a/src/as.h b/src/as.h new file mode 100644 index 0000000..1aefe30 --- /dev/null +++ b/src/as.h @@ -0,0 +1,43 @@ +/* + libloc - A library to determine the location of someone on the Internet + + Copyright (C) 2017 IPFire Development Team + + 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_AS_H +#define LIBLOC_AS_H + +#include + +#include +#include + +#include "stringpool.h" + +struct loc_as; +int loc_as_new(struct loc_ctx* ctx, struct loc_stringpool* pool, struct loc_as** as, uint32_t number); +struct loc_as* loc_as_ref(struct loc_as* as); +struct loc_as* loc_as_unref(struct loc_as* as); + +uint32_t loc_as_get_number(struct loc_as* as); + +const char* loc_as_get_name(struct loc_as* as); +int loc_as_set_name(struct loc_as* as, const char* name); + +int loc_as_cmp(struct loc_as* as1, struct loc_as* as2); + +int loc_as_new_from_database_v0(struct loc_ctx* ctx, struct loc_stringpool* pool, + struct loc_as** as, const struct loc_database_as_v0* dbobj); +int loc_as_to_database_v0(struct loc_as* as, struct loc_database_as_v0* dbobj); + +#endif diff --git a/src/database.c b/src/database.c index 85e3912..e81ea44 100644 --- a/src/database.c +++ b/src/database.c @@ -24,8 +24,10 @@ #include #include +#include #include "libloc-private.h" +#include "as.h" #include "database.h" #include "stringpool.h" @@ -37,6 +39,10 @@ struct loc_database { off_t vendor; off_t description; + // ASes in the database + struct loc_as** as; + size_t as_count; + struct loc_stringpool* pool; }; @@ -50,18 +56,6 @@ struct loc_database_magic { uint8_t version; }; -struct loc_database_header_v0 { - // Vendor who created the database - uint32_t vendor; - - // Description of the database - uint32_t description; - - // Tells us where the pool starts - uint32_t pool_offset; - uint32_t pool_length; -}; - LOC_EXPORT int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, size_t pool_size) { struct loc_database* db = calloc(1, sizeof(*db)); if (!db) @@ -102,6 +96,14 @@ LOC_EXPORT struct loc_database* loc_database_ref(struct loc_database* db) { static void loc_database_free(struct loc_database* db) { DEBUG(db->ctx, "Releasing database %p\n", db); + // Remove references to all ASes + if (db->as) { + for (unsigned int i = 0; i < db->as_count; i++) { + loc_as_unref(db->as[i]); + } + free(db->as); + } + loc_stringpool_unref(db->pool); loc_unref(db->ctx); @@ -144,6 +146,62 @@ LOC_EXPORT int loc_database_set_description(struct loc_database* db, const char* return 0; } +LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) { + return db->as_count; +} + +static int loc_database_has_as(struct loc_database* db, struct loc_as* as) { + for (unsigned int i = 0; i < db->as_count; i++) { + if (loc_as_cmp(as, db->as[i]) == 0) + return i; + } + + return -1; +} + +static int __loc_as_cmp(const void* as1, const void* as2) { + return loc_as_cmp(*(struct loc_as**)as1, *(struct loc_as**)as2); +} + +static void loc_database_sort_ases(struct loc_database* db) { + qsort(db->as, db->as_count, sizeof(*db->as), __loc_as_cmp); +} + +static struct loc_as* __loc_database_add_as(struct loc_database* db, struct loc_as* as) { + // Check if AS exists already + int i = loc_database_has_as(db, as); + if (i >= 0) { + loc_as_unref(as); + + // Select already existing AS + as = db->as[i]; + + return loc_as_ref(as); + } + + db->as_count++; + + // Make space for the new entry + db->as = realloc(db->as, sizeof(*db->as) * db->as_count); + + // Add the new entry at the end + db->as[db->as_count - 1] = loc_as_ref(as); + + // Sort everything + loc_database_sort_ases(db); + + return as; +} + +LOC_EXPORT struct loc_as* loc_database_add_as(struct loc_database* db, uint32_t number) { + struct loc_as* as; + int r = loc_as_new(db->ctx, db->pool, &as, number); + if (r) + return NULL; + + return __loc_database_add_as(db, as); +} + static int loc_database_read_magic(struct loc_database* db, FILE* f) { struct loc_database_magic magic; @@ -174,6 +232,40 @@ static int loc_database_read_magic(struct loc_database* db, FILE* f) { return 1; } +static int loc_database_read_as_section_v0(struct loc_database* db, + off_t as_offset, size_t as_length, FILE* f) { + struct loc_database_as_v0 dbobj; + + // Read from the start of the section + int r = fseek(f, as_offset, SEEK_SET); + if (r) + return r; + + // Read all ASes + size_t as_count = as_length / sizeof(dbobj); + for (unsigned int i = 0; i < as_count; i++) { + size_t bytes_read = fread(&dbobj, 1, sizeof(dbobj), f); + if (bytes_read < sizeof(dbobj)) { + ERROR(db->ctx, "Could not read an AS object\n"); + return -ENOMSG; + } + + // Allocate a new AS + struct loc_as* as; + r = loc_as_new_from_database_v0(db->ctx, db->pool, &as, &dbobj); + if (r) + return r; + + // Attach it to the database + as = __loc_database_add_as(db, as); + loc_as_unref(as); + } + + INFO(db->ctx, "Read %zu ASes from the database\n", db->as_count); + + return 0; +} + static int loc_database_read_header_v0(struct loc_database* db, FILE* f) { struct loc_database_header_v0 header; @@ -197,6 +289,14 @@ static int loc_database_read_header_v0(struct loc_database* db, FILE* f) { if (r) return r; + // AS section + off_t as_offset = ntohl(header.as_offset); + size_t as_length = ntohl(header.as_length); + + r = loc_database_read_as_section_v0(db, as_offset, as_length, f); + if (r) + return r; + return 0; } @@ -266,6 +366,19 @@ LOC_EXPORT int loc_database_write(struct loc_database* db, FILE* f) { } offset += sizeof(header); + // Write all ASes + header.as_offset = htonl(offset); + + struct loc_database_as_v0 dbas; + for (unsigned int i = 0; i < db->as_count; i++) { + // Convert AS into database format + loc_as_to_database_v0(db->as[i], &dbas); + + // Write to disk + offset += fwrite(&dbas, 1, sizeof(dbas), f); + } + header.as_length = htonl(db->as_count * sizeof(dbas)); + // Save the offset of the pool section DEBUG(db->ctx, "Pool starts at %jd bytes\n", offset); header.pool_offset = htonl(offset); diff --git a/src/database.h b/src/database.h index 72cda5a..41142ab 100644 --- a/src/database.h +++ b/src/database.h @@ -18,9 +18,12 @@ #define LIBLOC_DATABASE_H #include +#include #include +#include "as.h" + struct loc_database; int loc_database_new(struct loc_ctx* ctx, struct loc_database** db, size_t pool_size); int loc_database_open(struct loc_ctx* ctx, struct loc_database** database, FILE* f); @@ -32,6 +35,9 @@ int loc_database_set_vendor(struct loc_database* db, const char* vendor); const char* loc_database_get_description(struct loc_database* db); int loc_database_set_description(struct loc_database* db, const char* description); +size_t loc_database_count_as(struct loc_database* db); +struct loc_as* loc_database_add_as(struct loc_database* db, uint32_t number); + int loc_database_read(struct loc_database* db, FILE* f); int loc_database_write(struct loc_database* db, FILE* f); diff --git a/src/libloc.sym b/src/libloc.sym index 2a527f1..175c3fa 100644 --- a/src/libloc.sym +++ b/src/libloc.sym @@ -1,6 +1,16 @@ LIBLOC_PRIVATE { global: + # AS + loc_as_get_name; + loc_as_get_number; + loc_as_new; + loc_as_ref; + loc_as_set_name; + loc_as_unref; + # Database + loc_database_add_as; + loc_database_count_as; loc_database_get_description; loc_database_get_vendor; loc_database_new; diff --git a/src/loc/format.h b/src/loc/format.h new file mode 100644 index 0000000..deec7f1 --- /dev/null +++ b/src/loc/format.h @@ -0,0 +1,46 @@ +/* + libloc - A library to determine the location of someone on the Internet + + Copyright (C) 2017 IPFire Development Team + + 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_FORMAT_H +#define LIBLOC_FORMAT_H + +#include + +struct loc_database_header_v0 { + // Vendor who created the database + uint32_t vendor; + + // Description of the database + uint32_t description; + + // Tells us where the ASes start + uint32_t as_offset; + uint32_t as_length; + + // Tells us where the pool starts + uint32_t pool_offset; + uint32_t pool_length; +}; + +struct loc_database_as_v0 { + // The AS number + uint32_t number; + + // Name + uint32_t name; +}; + +#endif diff --git a/src/stringpool.h b/src/stringpool.h index dbe82c8..c3dffdd 100644 --- a/src/stringpool.h +++ b/src/stringpool.h @@ -18,6 +18,7 @@ #define LIBLOC_STRINGPOOL_H #include +#include #include diff --git a/src/test-as.c b/src/test-as.c new file mode 100644 index 0000000..1e1c91e --- /dev/null +++ b/src/test-as.c @@ -0,0 +1,88 @@ +/* + libloc - A library to determine the location of someone on the Internet + + Copyright (C) 2017 IPFire Development Team + + 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 +#include +#include + +#include +#include "database.h" + +#define TEST_AS_COUNT 100 + +int main(int argc, char** argv) { + int err; + + struct loc_ctx* ctx; + err = loc_new(&ctx); + if (err < 0) + exit(EXIT_FAILURE); + + // Create a database + struct loc_database* db; + err = loc_database_new(ctx, &db, 1024); + if (err < 0) + exit(EXIT_FAILURE); + + char name[256]; + for (unsigned int i = 1; i <= TEST_AS_COUNT; i++) { + struct loc_as* as = loc_database_add_as(db, i); + + sprintf(name, "Test AS%u", i); + loc_as_set_name(as, name); + + loc_as_unref(as); + } + + 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_database_write(db, f); + if (err) { + fprintf(stderr, "Could not write database: %s\n", strerror(-err)); + exit(EXIT_FAILURE); + } + fclose(f); + + loc_database_unref(db); + + // 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); + } + + err = loc_database_open(ctx, &db, f); + if (err) { + fprintf(stderr, "Could not open database: %s\n", strerror(-err)); + exit(EXIT_FAILURE); + } + + size_t as_count = loc_database_count_as(db); + if (as_count != TEST_AS_COUNT) { + fprintf(stderr, "Could not read all ASes\n"); + exit(EXIT_FAILURE); + } + + loc_database_unref(db); + loc_unref(ctx); + + return EXIT_SUCCESS; +} -- 2.39.2