1 /*
2  * Project    : ipv6calc
3  * File       : databases/lib/libipv6calc_db_wrapper_External.c
4  * Version    : $Id: afd04f01f138a78ccc19870e078208e13ec1a8e7 $
5  * Copyright  : 2013-2021 by Peter Bieringer <pb (at) bieringer.de>
6  *
7  * Information:
8  *  ipv6calc External (superseeding BuiltIn) database wrapper
9  */
10 
11 #include <stdio.h>
12 #include <string.h>
13 #include <dlfcn.h>
14 #include <unistd.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <time.h>
18 
19 #include "config.h"
20 
21 #include "libipv6calcdebug.h"
22 #include "libipv6calc.h"
23 
24 #include "libipv6calc_db_wrapper.h"
25 
26 
27 #ifdef SUPPORT_EXTERNAL
28 
29 #include <db.h>
30 
31 #include "libipv6calc_db_wrapper_External.h"
32 
33 char external_db_dir[PATH_MAX] = EXTERNAL_DB;
34 
35 static const char* wrapper_external_info = "External";
36 
37 
38 /* database usage map */
39 #define EXTERNAL_DB_MAX_BLOCKS_32	2	// 0-63
40 static uint32_t external_db_usage_map[EXTERNAL_DB_MAX_BLOCKS_32];
41 
42 #define EXTERNAL_DB_USAGE_MAP_TAG(db)	if (db < (32 * EXTERNAL_DB_MAX_BLOCKS_32)) { \
43 							DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Tag usage for db: %d", db); \
44 							external_db_usage_map[db / 32] |= 1 << (db % 32); \
45 						} else { \
46 							fprintf(stderr, "FIXME: unsupported db value (exceed limit): %d (%d)\n", db, 32 * EXTERNAL_DB_MAX_BLOCKS_32 - 1); \
47 							exit(1); \
48 						};
49 
50 char external_db_usage_string[IPV6CALC_STRING_MAX] = "";
51 
52 // local cache
53 #define IPV6CALC_DBD_SUBDB_MAX 3
54 static DB *db_ptr_cache[MAXENTRIES_ARRAY(libipv6calc_db_wrapper_External_db_file_desc)][IPV6CALC_DBD_SUBDB_MAX];
55 static db_recno_t db_recno_max_cache[MAXENTRIES_ARRAY(libipv6calc_db_wrapper_External_db_file_desc)][IPV6CALC_DBD_SUBDB_MAX];
56 
57 // creation time of databases
58 time_t wrapper_db_unixtime_External[MAXENTRIES_ARRAY(libipv6calc_db_wrapper_External_db_file_desc)];
59 
60 
61 // local prototyping
62 static char     *libipv6calc_db_wrapper_External_dbfilename(unsigned int type);
63 static char     *libipv6calc_db_wrapper_External_database_info(unsigned int type);
64 
65 
66 /*
67  * function initialise the External wrapper
68  *
69  * in : (nothing)
70  * out: 0=ok, 1=error
71  */
libipv6calc_db_wrapper_External_wrapper_init(void)72 int libipv6calc_db_wrapper_External_wrapper_init(void) {
73 	int i, j;
74 	char *result;
75 	DB *dbp;
76 	long int recno_max;
77 
78 	DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "Called");
79 
80 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Check for External databases in directory: %s", external_db_dir);
81 
82 	/* check available databases for resolution */
83 	for (i = 0; i < MAXENTRIES_ARRAY(libipv6calc_db_wrapper_External_db_file_desc); i++) {
84 		// clean local cache
85 		for (j = 0; j < IPV6CALC_DBD_SUBDB_MAX; j++) {
86 			db_ptr_cache[i][j] = NULL;
87 			db_recno_max_cache[i][j] = -1;
88 		};
89 		wrapper_db_unixtime_External[i] = 0;
90 
91 		// add features to implemented
92 		wrapper_features_by_source_implemented[IPV6CALC_DB_SOURCE_EXTERNAL] |= libipv6calc_db_wrapper_External_db_file_desc[i].features;
93 
94 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "External database test for availability: %s", libipv6calc_db_wrapper_External_db_file_desc[i].filename);
95 
96 		if (libipv6calc_db_wrapper_External_db_avail(libipv6calc_db_wrapper_External_db_file_desc[i].number) != 1) {
97 			// no file found
98 			continue;
99 		};
100 
101 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "External database available: %s type=%d", libipv6calc_db_wrapper_External_db_file_desc[i].description, libipv6calc_db_wrapper_External_db_file_desc[i].number);
102 
103 		result = libipv6calc_db_wrapper_External_database_info(libipv6calc_db_wrapper_External_db_file_desc[i].number);
104 
105 		if (strlen(result) == 0) {
106 			// no proper database
107 			continue;
108 		};
109 
110 		if (wrapper_db_unixtime_External[i] == 0) {
111 			// no proper database
112 			continue;
113 		};
114 
115 		// finally mark database features as available
116 		wrapper_features_by_source[IPV6CALC_DB_SOURCE_EXTERNAL] |= libipv6calc_db_wrapper_External_db_file_desc[i].features;
117 
118 		// more sophisticated check for "data-info"
119 		if (libipv6calc_db_wrapper_External_db_file_desc[i].number == EXTERNAL_DB_IPV4_REGISTRY) {
120 			dbp = libipv6calc_db_wrapper_External_open_type(EXTERNAL_DB_IPV4_REGISTRY | 0x40000, &recno_max);
121 			if (dbp == NULL) {
122 				// disable feature
123 				wrapper_features_by_source[IPV6CALC_DB_SOURCE_EXTERNAL] &= ~IPV6CALC_DB_IPV4_TO_INFO;
124 			};
125 		} else if (libipv6calc_db_wrapper_External_db_file_desc[i].number == EXTERNAL_DB_IPV6_REGISTRY) {
126 			dbp = libipv6calc_db_wrapper_External_open_type(EXTERNAL_DB_IPV6_REGISTRY | 0x40000, &recno_max);
127 			if (dbp == NULL) {
128 				// disable feature
129 				wrapper_features_by_source[IPV6CALC_DB_SOURCE_EXTERNAL] &= ~IPV6CALC_DB_IPV6_TO_INFO;
130 			};
131 		};
132 	};
133 
134 	wrapper_features |= wrapper_features_by_source[IPV6CALC_DB_SOURCE_EXTERNAL];
135 
136 	return 0;
137 };
138 
139 
140 /*
141  * wrapper: External_close
142  */
libipv6calc_db_wrapper_External_close(DB * dbp)143 static int libipv6calc_db_wrapper_External_close(DB *dbp) {
144 	int i, j;
145 
146 	DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "Called");
147 
148 	if (dbp != NULL) {
149 		/* cleanup cache entry */
150 		for (i = 0; i < MAXENTRIES_ARRAY(libipv6calc_db_wrapper_External_db_file_desc); i++) {
151 			for (j = 0; j < IPV6CALC_DBD_SUBDB_MAX; j++) {
152 				if (db_ptr_cache[i][j] == dbp) {
153 					db_ptr_cache[i][j] = NULL;
154 					db_recno_max_cache[i][j] = -1;
155 				};
156 			};
157 		};
158 
159 		dbp->close(dbp, 0);
160 	};
161 
162 	return(0);
163 };
164 
165 
166 /*
167  * function cleanup the External wrapper
168  *
169  * in : (nothing)
170  * out: 0=ok, 1=error
171  */
libipv6calc_db_wrapper_External_wrapper_cleanup(void)172 int libipv6calc_db_wrapper_External_wrapper_cleanup(void) {
173 	int i, j;
174 
175 	DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "Called");
176 
177 	for (i = 0; i < MAXENTRIES_ARRAY(libipv6calc_db_wrapper_External_db_file_desc); i++) {
178 		if (db_ptr_cache[i] != NULL) {
179 			DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Close External: type=%d desc='%s'", libipv6calc_db_wrapper_External_db_file_desc[i].number, libipv6calc_db_wrapper_External_db_file_desc[i].description);
180 			for (j = 0; j < IPV6CALC_DBD_SUBDB_MAX; j++) {
181 				libipv6calc_db_wrapper_External_close(db_ptr_cache[i][j]);
182 			};
183 		};
184 	};
185 
186 	DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "Finished");
187 	return 0;
188 };
189 
190 
191 /*
192  * function info of External wrapper
193  *
194  * in : ptr and size of string to be filled
195  * out: modified string;
196  */
libipv6calc_db_wrapper_External_wrapper_info(char * string,const size_t size)197 void libipv6calc_db_wrapper_External_wrapper_info(char* string, const size_t size) {
198 	DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "Called");
199 
200 	snprintf(string, size, "External available databases: Country4=%d Country6=%d IPV4_REG=%d IPV6_REG=%d", \
201 		(wrapper_features & IPV6CALC_DB_IPV4_TO_CC) ? 1 : 0, \
202 		(wrapper_features & IPV6CALC_DB_IPV6_TO_CC) ? 1 : 0, \
203 		(wrapper_features & IPV6CALC_DB_IPV4_TO_REGISTRY) ? 1 : 0, \
204 		(wrapper_features & IPV6CALC_DB_IPV6_TO_REGISTRY) ? 1 : 0 \
205 	);
206 
207 	DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "Finished");
208 	return;
209 };
210 
211 /*
212  * function print database info of External wrapper
213  *
214  * in : (void)
215  * out: (void)
216  */
libipv6calc_db_wrapper_External_wrapper_print_db_info(const int level_verbose,const char * prefix_string)217 void libipv6calc_db_wrapper_External_wrapper_print_db_info(const int level_verbose, const char *prefix_string) {
218 	int i, type, count = 0;
219 
220 	const char *prefix = "\0";
221 	if (prefix_string != NULL) {
222 		prefix = prefix_string;
223 	};
224 
225 	DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "Called");
226 
227 	IPV6CALC_DB_FEATURE_INFO(prefix, IPV6CALC_DB_SOURCE_EXTERNAL)
228 
229 	fprintf(stderr, "%sExternal: info of available databases in directory: %s\n", prefix, external_db_dir);
230 
231 	for (i = 0; i < MAXENTRIES_ARRAY(libipv6calc_db_wrapper_External_db_file_desc); i++) {
232 		type = libipv6calc_db_wrapper_External_db_file_desc[i].number;
233 
234 		if (libipv6calc_db_wrapper_External_db_avail(type)) {
235 			fprintf(stderr, "%sExternal: %-20s: %-40s (%s)\n", prefix, libipv6calc_db_wrapper_External_db_file_desc[i].description, libipv6calc_db_wrapper_External_db_file_desc[i].filename, libipv6calc_db_wrapper_External_database_info(type));
236 			count++;
237 		} else {
238 			if (level_verbose == LEVEL_VERBOSE2) {
239 				fprintf(stderr, "%sExternal: %-20s: %-40s (%s)\n", prefix, libipv6calc_db_wrapper_External_db_file_desc[i].description, libipv6calc_db_wrapper_External_dbfilename(type), strerror(errno));
240 			};
241 			continue;
242 		};
243 	};
244 
245 	if (count == 0) {
246 		fprintf(stderr, "%sExternal: NO available databases found in directory: %s\n", prefix, external_db_dir);
247 	};
248 
249 	DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "Finished");
250 	return;
251 };
252 
253 
254 /*
255  * wrapper: string regarding used database infos
256  */
libipv6calc_db_wrapper_External_wrapper_db_info_used(void)257 char *libipv6calc_db_wrapper_External_wrapper_db_info_used(void) {
258 	int type, i;
259 	char tempstring[IPV6CALC_STRING_MAX];
260 	char *info;
261 
262 	DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "Called");
263 
264 	for (i = 0; i < EXTERNAL_DB_MAX_BLOCKS_32; i++) {
265 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "external_db_usage_map[%d]=%08x", i, (unsigned int) external_db_usage_map[i]);
266 	};
267 
268 	for (type = 0; type < 32 * EXTERNAL_DB_MAX_BLOCKS_32; type++) {
269 		if ((external_db_usage_map[type / 32] & (1 << (type % 32))) != 0) {
270 			DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "DB type used: %d", type);
271 
272 			info = libipv6calc_db_wrapper_External_database_info(type);
273 
274 			if (info == NULL) { continue; }; // NULL pointer returned
275 
276 			if (strlen(info) == 0) { continue; }; // empty string returned
277 
278 			DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "type=%d info=%s", type, info);
279 
280 			if (strlen(external_db_usage_string) > 0) {
281 				if (strstr(external_db_usage_string, info) != NULL) {
282 					DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "type=%d info=%s (skip, already displayed)", type, info);
283 					continue;
284 				}; // string already included
285 
286 				snprintf(tempstring, sizeof(tempstring), "%s / %s", external_db_usage_string, info);
287 			} else {
288 				snprintf(tempstring, sizeof(tempstring), "%s", info);
289 			};
290 
291 			snprintf(external_db_usage_string, sizeof(external_db_usage_string), "%s", tempstring);
292 			DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "type=%d external_db_usage_string=%s", type, external_db_usage_string);
293 		};
294 	};
295 
296 	return(external_db_usage_string);
297 };
298 
299 
300 /*******************************
301  * Wrapper extension functions for External
302  *******************************/
303 
304 /*
305  * wrapper extension: External_dbfilename
306  */
libipv6calc_db_wrapper_External_dbfilename(unsigned int type)307 static char *libipv6calc_db_wrapper_External_dbfilename(unsigned int type) {
308 	static char tempstring[IPV6CALC_STRING_MAX];
309 	int  entry = -1, i;
310 
311 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Called: %s type=%d", wrapper_external_info, type);
312 
313 	for (i = 0; i < MAXENTRIES_ARRAY(libipv6calc_db_wrapper_External_db_file_desc); i++) {
314 		if (libipv6calc_db_wrapper_External_db_file_desc[i].number == type) {
315 			entry = i;
316 			break;
317 		};
318 	};
319 
320 	if (entry < 0) {
321 		return(NULL);
322 	};
323 
324 	snprintf(tempstring, sizeof(tempstring), "%s/%s", external_db_dir, libipv6calc_db_wrapper_External_db_file_desc[i].filename);
325 
326 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Finished: %s type=%d has filename=%s", wrapper_external_info, type, tempstring);
327 
328 	return(tempstring);
329 };
330 
331 
332 /*
333  * wrapper extension: External_dbdescription
334  */
libipv6calc_db_wrapper_External_dbdescription(const unsigned int type)335 const char *libipv6calc_db_wrapper_External_dbdescription(const unsigned int type) {
336 	int  entry = -1;
337 	unsigned int i;
338 
339 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Called: %s type=%d", wrapper_external_info, type);
340 
341 	for (i = 0; i < sizeof(libipv6calc_db_wrapper_External_db_file_desc) / sizeof(libipv6calc_db_wrapper_External_db_file_desc[0]); i++) {
342 		if (libipv6calc_db_wrapper_External_db_file_desc[i].number == type) {
343 			entry = i;
344 			break;
345 		};
346 	};
347 
348 	if (entry < 0) {
349 		return("unknown");
350 	};
351 
352 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Finished: %s type=%d has description=%s", wrapper_external_info, type, libipv6calc_db_wrapper_External_db_file_desc[i].description);
353 
354 	return(libipv6calc_db_wrapper_External_db_file_desc[i].description);
355 };
356 
357 
358 /*
359  * wrapper extension: External_db_avail
360  * ret: 1=avail  0=not-avail
361  */
libipv6calc_db_wrapper_External_db_avail(const unsigned int type)362 int libipv6calc_db_wrapper_External_db_avail(const unsigned int type) {
363 	char *filename;
364 	int r = 0;
365 
366 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Called: %s type=%d", wrapper_external_info, type);
367 
368 	filename = libipv6calc_db_wrapper_External_dbfilename(type);
369 
370 	if (filename == NULL) {
371 		goto END_libipv6calc_db_wrapper;
372 	};
373 
374 	r = (access(filename, R_OK) == 0) ? 1:0;
375 
376 	if (r == 0) {
377 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Finished: %s type=%d (still unknown) (r=%d: %s)", wrapper_external_info, type, r, strerror(errno));
378 	} else {
379 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Finished: %s type=%d (%s) (r=%d)", wrapper_external_info, type, filename, r);
380 	};
381 
382 END_libipv6calc_db_wrapper:
383 	return(r);
384 };
385 
386 
387 /*
388  * wrapper extension: External_open_type
389  * input:
390  * 	type (mandatory)
391  * 		if | 0x10000 -> info is opened and ptr is not cached
392  * 		if | 0x20000 -> data-iana is opened
393  * 		if | 0x40000 -> data-info is opened
394  * 	db_recno_max_ptr (set if not NULL)
395  */
libipv6calc_db_wrapper_External_open_type(const unsigned int type_flag,long int * db_recno_max_ptr)396 DB *libipv6calc_db_wrapper_External_open_type(const unsigned int type_flag, long int *db_recno_max_ptr) {
397 	DB *dbp = NULL;
398 	DBC *dbcp;
399 	DBT key, data;
400 
401 	int type = (type_flag & 0xffff);
402 	int info_selector = ((type_flag & 0x10000) != 0) ? 1 : 0;
403 	int data_iana_selector = ((type_flag & 0x20000) != 0) ? 1 : 0;
404 	int data_info_selector = ((type_flag & 0x40000) != 0) ? 1 : 0;
405 	int subdb = 0; // data
406 
407 	char *filename;
408 	int entry = -1, i;
409 	int ret;
410 
411 	const char *type_text;
412 	if (info_selector != 0) {
413 		type_text = "info";
414 	} else if (data_iana_selector != 0) {
415 		type_text = "data-iana";
416 		subdb = 1;
417 	} else if (data_info_selector != 0) {
418 		type_text = "data-info";
419 		subdb = 2;
420 	} else {
421 		type_text = "data";
422 	};
423 
424 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Called: %s type=%d (%s)", wrapper_external_info, type, type_text);
425 
426 	// check for valid type
427 	for (i = 0; i < MAXENTRIES_ARRAY(libipv6calc_db_wrapper_External_db_file_desc); i++) {
428 		if (libipv6calc_db_wrapper_External_db_file_desc[i].number == (type & 0xffff)) {
429 			entry = i;
430 			break;
431 		};
432 	};
433 
434 	if (entry < 0) {
435 		return(NULL);
436 	};
437 
438 	if ((info_selector == 0) && (db_ptr_cache[entry][subdb] != NULL)) {
439 		// already open
440 		dbp = db_ptr_cache[entry][subdb];
441 
442 		if (db_recno_max_ptr != NULL) {
443 			*db_recno_max_ptr = db_recno_max_cache[entry][subdb];
444 		};
445 
446 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Database already opened (cached) dbp=%p type=%d subdb=%d recno_max: %u", dbp, type, subdb, db_recno_max_cache[entry][subdb]);
447 
448 		goto END_libipv6calc_db_wrapper;
449 	};
450 
451 	// retrieve filename
452 	filename = libipv6calc_db_wrapper_External_dbfilename(type);
453 
454 	if (filename == NULL) {
455 		return(NULL);
456 	};
457 
458 	if (libipv6calc_db_wrapper_External_db_avail(type) != 1) {
459 		return(NULL);
460 
461 	};
462 
463 	if ((ret = db_create(&dbp, NULL, 0)) != 0) {
464 		if (ipv6calc_quiet == 0) {
465 			fprintf(stderr, "db_create: %s\n", db_strerror(ret));
466 		};
467 		return(NULL);
468 	};
469 
470 	if ((ret = dbp->open(dbp, NULL, filename, type_text, (info_selector == 0) ? DB_RECNO : DB_BTREE, DB_RDONLY, 0444)) != 0) {
471 		if ((ipv6calc_quiet == 0) && (data_info_selector == 0)) {
472 			fprintf(stderr, "db->open failed: %s (%s) subdb=%s\n", db_strerror(ret), filename, type_text);
473 		};
474 		return(NULL);
475 	};
476 
477 	if (info_selector == 0) {
478 		// cache entry
479 		db_ptr_cache[entry][subdb] = dbp;
480 
481 		// get amount of entries in database
482 		memset(&key, 0, sizeof(key));
483 		memset(&data, 0, sizeof(data));
484 
485 		/* Acquire a cursor for the database. */
486 		if ((ret = dbp->cursor(dbp, NULL, &dbcp, 0)) != 0) {
487 			dbp->err(dbp, ret, "DB->cursor");
488 			goto END_libipv6calc_db_wrapper_close_error;
489 		};
490 
491 		/* Walk through the database and print out the key/data pairs. */
492 		if ((ret = dbcp->c_get(dbcp, &key, &data, DB_LAST)) != 0) {
493 			dbp->err(dbp, ret, "DB->cursor/DB_LAST");
494 			goto END_libipv6calc_db_wrapper_close_error;
495 		};
496 
497 		/* Close the cursor. */
498 		if ((ret = dbcp->c_close(dbcp)) != 0) {
499 			dbp->err(dbp, ret, "DBcursor->close");
500 			goto END_libipv6calc_db_wrapper_close_error;
501 		};
502 
503 		db_recno_max_cache[entry][subdb] = *(db_recno_t *)key.data;
504 
505 		if (db_recno_max_cache[entry][subdb] < 2) {
506 			goto END_libipv6calc_db_wrapper_close_error;
507 		};
508 
509 		if (db_recno_max_ptr != NULL) {
510 			*db_recno_max_ptr = db_recno_max_cache[entry][subdb];
511 		};
512 
513 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Database successfully opened (fill-cache), dbp=%p type=%d subdb=%d recno_max=%u", dbp, type, subdb, db_recno_max_cache[entry][subdb]);
514 	} else {
515 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Database successfully opened, dbp=%p type=%d (info)", dbp, type);
516 	};
517 
518 	// jump to "good end"
519 	goto END_libipv6calc_db_wrapper;
520 
521 END_libipv6calc_db_wrapper_close_error:
522 	DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "error opening database, close now");
523 	libipv6calc_db_wrapper_External_close(dbp);
524 	dbp = NULL;
525 
526 END_libipv6calc_db_wrapper:
527 	return(dbp);
528 };
529 
530 
531 /*******************************
532  * Wrapper functions for External
533  *******************************/
534 
535 /*
536  * wrapper: External_database_info
537  */
libipv6calc_db_wrapper_External_database_info(const unsigned int type)538 char *libipv6calc_db_wrapper_External_database_info(const unsigned int type) {
539 	static char resultstring[IPV6CALC_STRING_MAX] = ""; // has to be static because pointer is returned
540 	char datastring[IPV6CALC_STRING_MAX];
541 	char tempstring[IPV6CALC_STRING_MAX];
542 	int ret, i, entry = -1;
543 	DB *dbp;
544 
545 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Called: %s", wrapper_external_info);
546 
547 	// check for valid type
548 	for (i = 0; i < MAXENTRIES_ARRAY(libipv6calc_db_wrapper_External_db_file_desc); i++) {
549 		if (libipv6calc_db_wrapper_External_db_file_desc[i].number == (type & 0xffff)) {
550 			entry = i;
551 			break;
552 		};
553 	};
554 
555 	if (entry < 0) {
556 		ERRORPRINT_WA("Invalid type (FIX CODE): %d", type);
557 		goto END_libipv6calc_db_wrapper;
558 	};
559 
560 	dbp = libipv6calc_db_wrapper_External_open_type(type | 0x10000, NULL);
561 
562 	if (dbp == NULL) {
563 		snprintf(resultstring, sizeof(resultstring), "%s", "(CAN'T OPEN database information)");
564 		goto END_libipv6calc_db_wrapper;
565 	};
566 
567 	// get dbusage
568 	ret = libipv6calc_db_wrapper_bdb_get_data_by_key(dbp, "dbusage", datastring, sizeof(datastring));
569 	if (ret != 0) {
570 		snprintf(resultstring, sizeof(resultstring), "%s", "can't retrieve 'dbusage', unsupported db file");
571 		goto END_libipv6calc_db_wrapper_close;
572 	};
573 	if (strcmp(datastring, "ipv6calc") != 0) {
574 		snprintf(resultstring, sizeof(resultstring), "%s", "dbusage!=ipv6calc, unsupported db file");
575 		goto END_libipv6calc_db_wrapper_close;
576 	};
577 
578 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Database dbusage string: %s", datastring);
579 
580 	// get dbdate
581 	ret = libipv6calc_db_wrapper_bdb_get_data_by_key(dbp, "dbdate", datastring, sizeof(datastring));
582 	if (ret != 0) {
583 		snprintf(resultstring, sizeof(resultstring), "%s", "can't retrieve 'dbdate', unsupported db file");
584 		goto END_libipv6calc_db_wrapper_close;
585 	};
586 
587 	snprintf(resultstring, sizeof(resultstring), "EXTDB-%d/%s", type, datastring);
588 
589 	// get dbcreated_unixtime
590 	ret = libipv6calc_db_wrapper_bdb_get_data_by_key(dbp, "dbcreated_unixtime", datastring, sizeof(datastring));
591 	if (ret != 0) {
592 		snprintf(resultstring, sizeof(resultstring), "%s", "can't retrieve 'dbcreated_unixtime', unsupported db file");
593 		goto END_libipv6calc_db_wrapper_close;
594 	};
595 
596 	wrapper_db_unixtime_External[entry] = atoi(datastring);
597 
598 	if (wrapper_db_unixtime_External[entry] == 0) {
599 		snprintf(resultstring, sizeof(resultstring), "%s", "'dbcreated_unixtime' is not proper, unsupported db file");
600 		goto END_libipv6calc_db_wrapper_close;
601 	};
602 
603 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "wrapper_db_unixtime_External=%ld", (long int) wrapper_db_unixtime_External[entry]);
604 
605 	strftime(datastring, sizeof(datastring), "%Y%m%d-%H%M%S UTC", gmtime(&wrapper_db_unixtime_External[entry]));
606 	snprintf(tempstring, sizeof(tempstring), "%s, created: %s", resultstring, datastring);
607 	snprintf(resultstring, sizeof(resultstring), "%s", tempstring);
608 
609 END_libipv6calc_db_wrapper_close:
610 	libipv6calc_db_wrapper_External_close(dbp);
611 
612 END_libipv6calc_db_wrapper:
613 	return(resultstring);
614 };
615 
616 
617 /*********************************************
618  * Abstract functions
619  * *******************************************/
620 
621 /* query for available features
622  * ret=-1: unknown
623  * 0 : not matching
624  * 1 : ok
625  */
libipv6calc_db_wrapper_External_has_features(uint32_t features)626 int libipv6calc_db_wrapper_External_has_features(uint32_t features) {
627 	int result = -1;
628 
629 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Called with feature value to test: 0x%08x", features);
630 
631 	if ((wrapper_features_by_source[IPV6CALC_DB_SOURCE_EXTERNAL] & features) == features) {
632 		result = 1;
633 	} else {
634 		result = 0;
635 	};
636 
637 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Return with result: %d", result);
638 	return(result);
639 };
640 
641 
642 /* query db_unixtime by feature
643  * ret=-1: unknown
644  * 0 : not matching
645  * 1 : ok
646  */
libipv6calc_db_wrapper_External_db_unixtime_by_feature(uint32_t feature)647 time_t libipv6calc_db_wrapper_External_db_unixtime_by_feature(uint32_t feature) {
648 	time_t result = 0;
649 	int i;
650 
651 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Called with feature value to get db_unixtime: 0x%08x", feature);
652 
653 	// run through entries
654 	for (i = 0; i < MAXENTRIES_ARRAY(libipv6calc_db_wrapper_External_db_file_desc); i++) {
655 		if ((libipv6calc_db_wrapper_External_db_file_desc[i].features & feature) == feature) {
656 			// found
657 			if (wrapper_db_unixtime_External[i] > result) {
658 				result = wrapper_db_unixtime_External[i];
659 			};
660 		};
661 	};
662 
663 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Return for feature=0x08x db_unixtime=%ld", (long int) result);
664 	return(result);
665 };
666 
667 
668 /*
669  * get registry number of an IPv4/IPv6 address
670  *
671  * in:  ipaddr
672  * out: assignment number (-1 = no result)
673  */
libipv6calc_db_wrapper_External_registry_num_by_addr(const ipv6calc_ipaddr * ipaddrp)674 int libipv6calc_db_wrapper_External_registry_num_by_addr(const ipv6calc_ipaddr *ipaddrp) {
675 	DB *dbp, *dbp_iana;
676 	long int recno_max;
677 	char resultstring[IPV6CALC_STRING_MAX];
678 	int i, result;
679 	int retval = REGISTRY_UNKNOWN;
680 
681 	int External_type;
682 
683 	switch (ipaddrp->proto) {
684 	    case IPV6CALC_PROTO_IPV4:
685 		External_type = EXTERNAL_DB_IPV4_REGISTRY;
686 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Given IPv4 address: %08x", (unsigned int) ipaddrp->addr[0]);
687 		break;
688 
689 	    case IPV6CALC_PROTO_IPV6:
690 		External_type = EXTERNAL_DB_IPV6_REGISTRY;
691 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Given IPv6 address prefix (0-63): %08x%08x", (unsigned int) ipaddrp->addr[0], (unsigned int) ipaddrp->addr[1]);
692 		break;
693 
694 	    default:
695 		ERRORPRINT_WA("unsupported protocol: %d (FIX CODE)", ipaddrp->proto);
696 		exit(EXIT_FAILURE);
697 		break;
698 	};
699 
700 
701 	// data (standard)
702 	dbp = libipv6calc_db_wrapper_External_open_type(External_type, &recno_max);
703 
704 	if (dbp == NULL) {
705 		DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "Error opening External by type");
706 		goto END_libipv6calc_db_wrapper;
707 	};
708 
709 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "database opened type=%d recno_max=%ld dbp=%p", External_type, recno_max, dbp);
710 
711 	result = libipv6calc_db_wrapper_get_entry_generic(
712 		(void *) dbp,							// pointer to database
713 		IPV6CALC_DB_LOOKUP_DATA_PTR_TYPE_BDB,				// type of data_ptr
714 		(ipaddrp->proto == IPV6CALC_PROTO_IPV4) \
715 		  ? IPV6CALC_DB_LOOKUP_DATA_KEY_TYPE_FIRST_LAST \
716 		  : IPV6CALC_DB_LOOKUP_DATA_KEY_TYPE_BASE_MASK,			// key type
717 		(ipaddrp->proto == IPV6CALC_PROTO_IPV4) \
718 		  ? IPV6CALC_DB_LOOKUP_DATA_DBD_FORMAT_SEMICOLON_SEP_HEX_32x2 \
719 		  : IPV6CALC_DB_LOOKUP_DATA_DBD_FORMAT_SEMICOLON_SEP_HEX_WITH_VALUE_32x4 ,	// key format
720 		(ipaddrp->proto == IPV6CALC_PROTO_IPV4) \
721 		  ? 32 \
722 		  : 64,								// key length
723 		(ipaddrp->proto == IPV6CALC_PROTO_IPV4) \
724 		  ? IPV6CALC_DB_LOOKUP_DATA_SEARCH_TYPE_BINARY \
725 		  : IPV6CALC_DB_LOOKUP_DATA_SEARCH_TYPE_SEQLONGEST,		// search type
726 		recno_max,							// number of rows
727 		ipaddrp->addr[0],						// lookup key MSB
728 		(ipaddrp->proto == IPV6CALC_PROTO_IPV4) \
729 		  ? 0 \
730 		  : ipaddrp->addr[1],						// lookup key LSB
731 		resultstring,							// data ptr
732 		NULL								// function pointer
733 	);
734 
735 	if (result >= 0 ) {
736 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "found match in database type=%d", External_type);
737 		goto END_libipv6calc_db_wrapper_match;
738 	};
739 
740 	if (ipaddrp->proto != IPV6CALC_PROTO_IPV4) {
741 		goto END_libipv6calc_db_wrapper;
742 	};
743 
744 	// data-iana (fallback for IPv4 only)
745 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "no found match in database type=%d, fallback to IANA data now for: %08x", External_type, ipaddrp->addr[0]);
746 	dbp_iana = libipv6calc_db_wrapper_External_open_type(External_type | 0x20000, &recno_max);
747 
748 	if (dbp == NULL) {
749 		DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "Error opening External by type");
750 		goto END_libipv6calc_db_wrapper;
751 	};
752 
753 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "database opened type=%d (data-iana) recno_max=%ld", External_type, recno_max);
754 
755 	result = libipv6calc_db_wrapper_get_entry_generic(
756 		(void *) dbp_iana,					// pointer to database
757 		IPV6CALC_DB_LOOKUP_DATA_PTR_TYPE_BDB,			// type of data_ptr
758 		IPV6CALC_DB_LOOKUP_DATA_KEY_TYPE_FIRST_LAST,		// key type
759 		IPV6CALC_DB_LOOKUP_DATA_DBD_FORMAT_SEMICOLON_SEP_HEX_32x2,   // key format
760 		32,							// key length
761 		IPV6CALC_DB_LOOKUP_DATA_SEARCH_TYPE_BINARY,		// search type
762 		recno_max,						// number of rows
763 		ipaddrp->addr[0],					// lookup key MSB
764 		0,							// lookup key LSB
765 		resultstring,						// data ptr
766 		NULL							// function pointer
767 	);
768 
769 	libipv6calc_db_wrapper_External_close(dbp_iana);
770 
771 	if (result >= 0 ) {
772 		goto END_libipv6calc_db_wrapper_match;
773 	};
774 
775 	goto END_libipv6calc_db_wrapper;
776 
777 END_libipv6calc_db_wrapper_match:
778 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "resultstring=%s", resultstring);
779 
780 	char datastring[IPV6CALC_STRING_MAX];
781 	snprintf(datastring, sizeof(datastring), "%s", resultstring); // copy string for strtok
782 
783 	char *token, *cptr, **ptrptr;
784 	ptrptr = &cptr;
785 
786 	int token_count = 0;
787 
788 	// split result string
789 	token = strtok_r(datastring, ";", ptrptr);
790 	while (token != NULL) {
791 		token_count++;
792 
793 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Database entry found %d: %s", token_count, token);
794 
795 		if (token_count == 1) {
796 			for (i = 0; i < MAXENTRIES_ARRAY(ipv6calc_registries); i++) {
797 				if (strcmp(token, ipv6calc_registries[i].tokensimple) == 0) {
798 					retval = ipv6calc_registries[i].number;
799 					break;
800 				};
801 			};
802 		};
803 
804 		/* get next token */
805 		token = strtok_r(NULL, ";", ptrptr);
806 	};
807 
808 	if (token_count != 1) {
809 		ERRORPRINT_WA("data has more entries than expected, corrupt database: %d (resultstring='%s' prefix=%08x%08x)", token_count, resultstring, (unsigned int) ipaddrp->addr[0], (unsigned int) ipaddrp->addr[1]);
810 		goto END_libipv6calc_db_wrapper;
811 	};
812 
813 	if (retval == REGISTRY_UNKNOWN) {
814 		DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "did not return a record for 'registry'");
815 		goto END_libipv6calc_db_wrapper;
816 	};
817 
818 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "result registry=%d", retval);
819 
820 	EXTERNAL_DB_USAGE_MAP_TAG(External_type);
821 
822 END_libipv6calc_db_wrapper:
823 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "retval=%d", retval);
824 	return(retval);
825 };
826 
827 
828 /*
829  * get country code of an IPv4/IPv6 address
830  *
831  * in:  ipaddr
832  * mod: country code
833  * out: status of retrievment (0=success, -1=problem)
834  */
libipv6calc_db_wrapper_External_country_code_by_addr(const ipv6calc_ipaddr * ipaddrp,char * country,const size_t country_len)835 int libipv6calc_db_wrapper_External_country_code_by_addr(const ipv6calc_ipaddr *ipaddrp, char *country, const size_t country_len) {
836 	DB *dbp;
837 	long int recno_max;
838 	static char resultstring[IPV6CALC_STRING_MAX];
839 	int result;
840 	int retval = -1;
841 
842 	int External_type;
843 
844 	switch (ipaddrp->proto) {
845 	    case IPV6CALC_PROTO_IPV4:
846 		External_type = EXTERNAL_DB_IPV4_COUNTRYCODE;
847 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Given IPv4 address: %08x", (unsigned int) ipaddrp->addr[0]);
848 		break;
849 
850 	    case IPV6CALC_PROTO_IPV6:
851 		External_type = EXTERNAL_DB_IPV6_COUNTRYCODE;
852 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Given IPv6 address prefix (0-63): %08x%08x", (unsigned int) ipaddrp->addr[0], (unsigned int) ipaddrp->addr[1]);
853 		break;
854 
855 	    default:
856 		ERRORPRINT_WA("unsupported protocol: %d (FIX CODE)", ipaddrp->proto);
857 		exit(EXIT_FAILURE);
858 		break;
859 	};
860 
861 
862 	// data (standard)
863 	dbp = libipv6calc_db_wrapper_External_open_type(External_type, &recno_max);
864 
865 	if (dbp == NULL) {
866 		DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "Error opening External by type");
867 		goto END_libipv6calc_db_wrapper;
868 	};
869 
870 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "database opened type=%d recno_max=%ld dbp=%p", External_type, recno_max, dbp);
871 
872 	result = libipv6calc_db_wrapper_get_entry_generic(
873 		(void *) dbp,							// pointer to database
874 		IPV6CALC_DB_LOOKUP_DATA_PTR_TYPE_BDB,				// type of data_ptr
875 		(ipaddrp->proto == IPV6CALC_PROTO_IPV4) \
876 		  ? IPV6CALC_DB_LOOKUP_DATA_KEY_TYPE_FIRST_LAST \
877 		  : IPV6CALC_DB_LOOKUP_DATA_KEY_TYPE_BASE_MASK,			// key type
878 		(ipaddrp->proto == IPV6CALC_PROTO_IPV4) \
879 		  ? IPV6CALC_DB_LOOKUP_DATA_DBD_FORMAT_SEMICOLON_SEP_HEX_32x2 \
880 		  : IPV6CALC_DB_LOOKUP_DATA_DBD_FORMAT_SEMICOLON_SEP_HEX_32x4 ,	// key format
881 		(ipaddrp->proto == IPV6CALC_PROTO_IPV4) \
882 		  ? 32 \
883 		  : 64,								// key length
884 		IPV6CALC_DB_LOOKUP_DATA_SEARCH_TYPE_BINARY,			// search type
885 		recno_max,							// number of rows
886 		ipaddrp->addr[0],						// lookup key MSB
887 		(ipaddrp->proto == IPV6CALC_PROTO_IPV4) \
888 		  ? 0 \
889 		  : ipaddrp->addr[1],						// lookup key LSB
890 		resultstring,							// data ptr
891 		NULL								// function pointer
892 	);
893 
894 	if (result < 0) {
895 		DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "no match found");
896 		goto END_libipv6calc_db_wrapper;
897 	};
898 
899 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "resultstring=%s", resultstring);
900 
901 	char datastring[IPV6CALC_STRING_MAX];
902 	snprintf(datastring, sizeof(datastring), "%s", resultstring); // copy string for strtok
903 
904 	char *token, *cptr, **ptrptr;
905 	ptrptr = &cptr;
906 
907 	int token_count = 0;
908 
909 	// split result string
910 	token = strtok_r(datastring, ";", ptrptr);
911 	while (token != NULL) {
912 		token_count++;
913 
914 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Database entry found %d: %s", token_count, token);
915 
916 		if (token_count == 1) {
917 			/* country */
918 			snprintf(country, country_len, "%s", token);
919 		};
920 
921 		/* get next token */
922 		token = strtok_r(NULL, ";", ptrptr);
923 	};
924 
925 	if (token_count != 1) {
926 		ERRORPRINT_WA("data has more entries than expected, corrupt database: %d (resultstring='%s' prefix=%08x%08x)", token_count, resultstring, (unsigned int) ipaddrp->addr[0], (unsigned int) ipaddrp->addr[1]);
927 		goto END_libipv6calc_db_wrapper;
928 	};
929 
930 	if (strlen(country) != 2) {
931 		DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "did not return a record for 'CountryCode'");
932 		goto END_libipv6calc_db_wrapper;
933 	};
934 
935 	retval = 0;
936 
937 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "result CountryCode=%s", country);
938 
939 	EXTERNAL_DB_USAGE_MAP_TAG(External_type);
940 
941 END_libipv6calc_db_wrapper:
942 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "retval=%d", retval);
943 	return(retval);
944 };
945 
946 
947 /*
948  * get info of an IPv4/IPv6 address
949  *  stored in REGISTRY db in dedicated table
950  *
951  * in:  ipaddr
952  * mod: string
953  * out: 0=OK
954  */
libipv6calc_db_wrapper_External_info_by_ipaddr(const ipv6calc_ipaddr * ipaddrp,char * string,const size_t string_len)955 int libipv6calc_db_wrapper_External_info_by_ipaddr(const ipv6calc_ipaddr *ipaddrp, char *string, const size_t string_len) {
956 	DB *dbp;
957 	long int recno_max;
958 	char resultstring[IPV6CALC_STRING_MAX];
959 	int result;
960 	int retval = -1;
961 
962 	int External_type;
963 
964 	switch (ipaddrp->proto) {
965 	    case IPV6CALC_PROTO_IPV4:
966 		External_type = EXTERNAL_DB_IPV4_REGISTRY;
967 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Given IPv4 address: %08x", (unsigned int) ipaddrp->addr[0]);
968 		break;
969 
970 	    case IPV6CALC_PROTO_IPV6:
971 		External_type = EXTERNAL_DB_IPV6_REGISTRY;
972 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Given IPv6 address prefix (0-63): %08x%08x", (unsigned int) ipaddrp->addr[0], (unsigned int) ipaddrp->addr[1]);
973 		break;
974 
975 	    default:
976 		ERRORPRINT_WA("unsupported protocol: %d (FIX CODE)", ipaddrp->proto);
977 		exit(EXIT_FAILURE);
978 		break;
979 	};
980 
981 
982 	// data-info
983 	dbp = libipv6calc_db_wrapper_External_open_type(External_type | 0x40000, &recno_max);
984 
985 	if (dbp == NULL) {
986 		DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "Error opening External by type");
987 		goto END_libipv6calc_db_wrapper;
988 	};
989 
990 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "database opened type=%x recno_max=%ld dbp=%p", External_type | 0x40000, recno_max, dbp);
991 
992 	result = libipv6calc_db_wrapper_get_entry_generic(
993 		(void *) dbp,							// pointer to database
994 		IPV6CALC_DB_LOOKUP_DATA_PTR_TYPE_BDB,				// type of data_ptr
995 		(ipaddrp->proto == IPV6CALC_PROTO_IPV4) \
996 		  ? IPV6CALC_DB_LOOKUP_DATA_KEY_TYPE_FIRST_LAST \
997 		  : IPV6CALC_DB_LOOKUP_DATA_KEY_TYPE_BASE_MASK,			// key type
998 		(ipaddrp->proto == IPV6CALC_PROTO_IPV4) \
999 		  ? IPV6CALC_DB_LOOKUP_DATA_DBD_FORMAT_SEMICOLON_SEP_HEX_32x2 \
1000 		  : IPV6CALC_DB_LOOKUP_DATA_DBD_FORMAT_SEMICOLON_SEP_HEX_32x4 ,	// key format
1001 		(ipaddrp->proto == IPV6CALC_PROTO_IPV4) \
1002 		  ? 32 \
1003 		  : 64,								// key length
1004 		IPV6CALC_DB_LOOKUP_DATA_SEARCH_TYPE_BINARY,			// search type
1005 		recno_max,							// number of rows
1006 		ipaddrp->addr[0],						// lookup key MSB
1007 		(ipaddrp->proto == IPV6CALC_PROTO_IPV4) \
1008 		  ? 0 \
1009 		  : ipaddrp->addr[1],						// lookup key LSB
1010 		resultstring,							// data ptr
1011 		NULL								// function pointer
1012 	);
1013 
1014 	if (result < 0) {
1015 		DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "no match found");
1016 		goto END_libipv6calc_db_wrapper;
1017 	};
1018 
1019 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "resultstring=%s", resultstring);
1020 
1021 	char datastring[IPV6CALC_STRING_MAX];
1022 	snprintf(datastring, sizeof(datastring), "%s", resultstring); // copy string for strtok
1023 
1024 	char *token, *cptr, **ptrptr;
1025 	ptrptr = &cptr;
1026 
1027 	int token_count = 0;
1028 
1029 	// split result string
1030 	token = strtok_r(datastring, ";", ptrptr);
1031 	while (token != NULL) {
1032 		token_count++;
1033 
1034 		DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "Database entry found %d: %s", token_count, token);
1035 
1036 		if (token_count == 1) {
1037 			/* info */
1038 			snprintf(string, string_len, "%s", token);
1039 		};
1040 
1041 		/* get next token */
1042 		token = strtok_r(NULL, ";", ptrptr);
1043 	};
1044 
1045 	if (token_count != 1) {
1046 		ERRORPRINT_WA("data has more entries than expected, corrupt database: %d (resultstring='%s' prefix=%08x%08x)", token_count, resultstring, (unsigned int) ipaddrp->addr[0], (unsigned int) ipaddrp->addr[1]);
1047 		goto END_libipv6calc_db_wrapper;
1048 	};
1049 
1050 	if (strlen(string) == 0) {
1051 		DEBUGPRINT_NA(DEBUG_libipv6calc_db_wrapper_External, "did not return a record for 'Info'");
1052 		goto END_libipv6calc_db_wrapper;
1053 	};
1054 
1055 	retval = 0;
1056 
1057 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "result Info=%s", string);
1058 
1059 	EXTERNAL_DB_USAGE_MAP_TAG(External_type);
1060 
1061 END_libipv6calc_db_wrapper:
1062 	DEBUGPRINT_WA(DEBUG_libipv6calc_db_wrapper_External, "retval=%d", retval);
1063 	return(retval);
1064 };
1065 
1066 
1067 #endif //SUPPORT_EXTERNAL
1068