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.
17 #include <arpa/inet.h>
24 #include <sys/types.h>
26 #include <loc/libloc.h>
27 #include <loc/format.h>
29 #include "libloc-private.h"
32 #include "stringpool.h"
42 // ASes in the database
46 struct loc_stringpool
* pool
;
49 const char* LOC_DATABASE_MAGIC
= "LOCDBXX";
50 unsigned int LOC_DATABASE_VERSION
= 0;
52 struct loc_database_magic
{
55 // Database version information
59 LOC_EXPORT
int loc_database_new(struct loc_ctx
* ctx
, struct loc_database
** database
, size_t pool_size
) {
60 struct loc_database
* db
= calloc(1, sizeof(*db
));
65 db
->ctx
= loc_ref(ctx
);
68 DEBUG(db
->ctx
, "Database allocated at %p\n", db
);
71 int r
= loc_stringpool_new(db
->ctx
, &db
->pool
, pool_size
);
73 loc_database_unref(db
);
82 LOC_EXPORT
int loc_database_open(struct loc_ctx
* ctx
, struct loc_database
** database
, FILE* f
) {
83 int r
= loc_database_new(ctx
, database
, 0);
87 return loc_database_read(*database
, f
);
90 LOC_EXPORT
struct loc_database
* loc_database_ref(struct loc_database
* db
) {
96 static void loc_database_free(struct loc_database
* db
) {
97 DEBUG(db
->ctx
, "Releasing database %p\n", db
);
99 // Remove references to all ASes
101 for (unsigned int i
= 0; i
< db
->as_count
; i
++) {
102 loc_as_unref(db
->as
[i
]);
107 loc_stringpool_unref(db
->pool
);
113 LOC_EXPORT
struct loc_database
* loc_database_unref(struct loc_database
* db
) {
114 if (--db
->refcount
> 0)
117 loc_database_free(db
);
121 LOC_EXPORT
const char* loc_database_get_vendor(struct loc_database
* db
) {
122 return loc_stringpool_get(db
->pool
, db
->vendor
);
125 LOC_EXPORT
int loc_database_set_vendor(struct loc_database
* db
, const char* vendor
) {
126 // Add the string to the string pool
127 off_t offset
= loc_stringpool_add(db
->pool
, vendor
);
135 LOC_EXPORT
const char* loc_database_get_description(struct loc_database
* db
) {
136 return loc_stringpool_get(db
->pool
, db
->description
);
139 LOC_EXPORT
int loc_database_set_description(struct loc_database
* db
, const char* description
) {
140 // Add the string to the string pool
141 off_t offset
= loc_stringpool_add(db
->pool
, description
);
145 db
->description
= offset
;
149 LOC_EXPORT
size_t loc_database_count_as(struct loc_database
* db
) {
153 static int loc_database_has_as(struct loc_database
* db
, struct loc_as
* as
) {
154 for (unsigned int i
= 0; i
< db
->as_count
; i
++) {
155 if (loc_as_cmp(as
, db
->as
[i
]) == 0)
162 static int __loc_as_cmp(const void* as1
, const void* as2
) {
163 return loc_as_cmp(*(struct loc_as
**)as1
, *(struct loc_as
**)as2
);
166 static void loc_database_sort_ases(struct loc_database
* db
) {
167 qsort(db
->as
, db
->as_count
, sizeof(*db
->as
), __loc_as_cmp
);
170 static struct loc_as
* __loc_database_add_as(struct loc_database
* db
, struct loc_as
* as
) {
171 // Check if AS exists already
172 int i
= loc_database_has_as(db
, as
);
176 // Select already existing AS
179 return loc_as_ref(as
);
184 // Make space for the new entry
185 db
->as
= realloc(db
->as
, sizeof(*db
->as
) * db
->as_count
);
187 // Add the new entry at the end
188 db
->as
[db
->as_count
- 1] = loc_as_ref(as
);
191 loc_database_sort_ases(db
);
196 LOC_EXPORT
struct loc_as
* loc_database_add_as(struct loc_database
* db
, uint32_t number
) {
198 int r
= loc_as_new(db
->ctx
, db
->pool
, &as
, number
);
202 return __loc_database_add_as(db
, as
);
205 static int loc_database_read_magic(struct loc_database
* db
, FILE* f
) {
206 struct loc_database_magic magic
;
209 size_t bytes_read
= fread(&magic
, 1, sizeof(magic
), f
);
211 // Check if we have been able to read enough data
212 if (bytes_read
< sizeof(magic
)) {
213 ERROR(db
->ctx
, "Could not read enough data to validate magic bytes\n");
214 DEBUG(db
->ctx
, "Read %zu bytes, but needed %zu\n", bytes_read
, sizeof(magic
));
218 // Compare magic bytes
219 if (memcmp(LOC_DATABASE_MAGIC
, magic
.magic
, strlen(LOC_DATABASE_MAGIC
)) == 0) {
220 DEBUG(db
->ctx
, "Magic value matches\n");
223 db
->version
= ntohs(magic
.version
);
224 DEBUG(db
->ctx
, "Database version is %u\n", db
->version
);
229 ERROR(db
->ctx
, "Database format is not compatible\n");
235 static int loc_database_read_as_section_v0(struct loc_database
* db
,
236 off_t as_offset
, size_t as_length
, FILE* f
) {
237 struct loc_database_as_v0 dbobj
;
239 // Read from the start of the section
240 int r
= fseek(f
, as_offset
, SEEK_SET
);
245 size_t as_count
= as_length
/ sizeof(dbobj
);
246 for (unsigned int i
= 0; i
< as_count
; i
++) {
247 size_t bytes_read
= fread(&dbobj
, 1, sizeof(dbobj
), f
);
248 if (bytes_read
< sizeof(dbobj
)) {
249 ERROR(db
->ctx
, "Could not read an AS object\n");
255 r
= loc_as_new_from_database_v0(db
->ctx
, db
->pool
, &as
, &dbobj
);
259 // Attach it to the database
260 as
= __loc_database_add_as(db
, as
);
264 INFO(db
->ctx
, "Read %zu ASes from the database\n", db
->as_count
);
269 static int loc_database_read_header_v0(struct loc_database
* db
, FILE* f
) {
270 struct loc_database_header_v0 header
;
273 size_t size
= fread(&header
, 1, sizeof(header
), f
);
275 if (size
< sizeof(header
)) {
276 ERROR(db
->ctx
, "Could not read enough data for header\n");
281 db
->vendor
= ntohl(header
.vendor
);
282 db
->description
= ntohl(header
.description
);
285 off_t pool_offset
= ntohl(header
.pool_offset
);
286 size_t pool_length
= ntohl(header
.pool_length
);
288 int r
= loc_stringpool_read(db
->pool
, f
, pool_offset
, pool_length
);
293 off_t as_offset
= ntohl(header
.as_offset
);
294 size_t as_length
= ntohl(header
.as_length
);
296 r
= loc_database_read_as_section_v0(db
, as_offset
, as_length
, f
);
303 static int loc_database_read_header(struct loc_database
* db
, FILE* f
) {
304 switch (db
->version
) {
306 return loc_database_read_header_v0(db
, f
);
309 ERROR(db
->ctx
, "Incompatible database version: %u\n", db
->version
);
314 LOC_EXPORT
int loc_database_read(struct loc_database
* db
, FILE* f
) {
315 int r
= fseek(f
, 0, SEEK_SET
);
320 r
= loc_database_read_magic(db
, f
);
325 r
= loc_database_read_header(db
, f
);
332 static void loc_database_make_magic(struct loc_database
* db
, struct loc_database_magic
* magic
) {
334 for (unsigned int i
= 0; i
< strlen(LOC_DATABASE_MAGIC
); i
++)
335 magic
->magic
[i
] = LOC_DATABASE_MAGIC
[i
];
338 magic
->version
= htons(LOC_DATABASE_VERSION
);
341 LOC_EXPORT
int loc_database_write(struct loc_database
* db
, FILE* f
) {
342 struct loc_database_magic magic
;
343 loc_database_make_magic(db
, &magic
);
346 struct loc_database_header_v0 header
;
347 header
.vendor
= htonl(db
->vendor
);
348 header
.description
= htonl(db
->description
);
353 // Start writing at the beginning of the file
354 r
= fseek(f
, 0, SEEK_SET
);
359 offset
+= fwrite(&magic
, 1, sizeof(magic
), f
);
361 // Skip the space we need to write the header later
362 r
= fseek(f
, sizeof(header
), SEEK_CUR
);
364 DEBUG(db
->ctx
, "Could not seek to position after header\n");
367 offset
+= sizeof(header
);
370 header
.as_offset
= htonl(offset
);
372 struct loc_database_as_v0 dbas
;
373 for (unsigned int i
= 0; i
< db
->as_count
; i
++) {
374 // Convert AS into database format
375 loc_as_to_database_v0(db
->as
[i
], &dbas
);
378 offset
+= fwrite(&dbas
, 1, sizeof(dbas
), f
);
380 header
.as_length
= htonl(db
->as_count
* sizeof(dbas
));
382 // Save the offset of the pool section
383 DEBUG(db
->ctx
, "Pool starts at %jd bytes\n", offset
);
384 header
.pool_offset
= htonl(offset
);
387 size_t pool_length
= loc_stringpool_write(db
->pool
, f
);
388 DEBUG(db
->ctx
, "Pool has a length of %zu bytes\n", pool_length
);
389 header
.pool_length
= htonl(pool_length
);
392 r
= fseek(f
, sizeof(magic
), SEEK_SET
);
396 offset
+= fwrite(&header
, 1, sizeof(header
), f
);