1 /* Copyright © 2012 Brandon L Black <blblack@gmail.com>
2 *
3 * This file is part of gdnsd.
4 *
5 * gdnsd-plugin-geoip is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * gdnsd-plugin-geoip is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with gdnsd. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include <config.h>
21 #include "gdgeoip.h"
22
23 #include <gdnsd/alloc.h>
24 #include <gdnsd/dmn.h>
25 #include <gdnsd/log.h>
26 #include <gdnsd/vscf.h>
27 #include <gdnsd/misc.h>
28 #include <gdnsd/file.h>
29
30 #include <inttypes.h>
31 #include <stdbool.h>
32 #include <stddef.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36
37 /*****************************************************************************
38 * This portion of the code in this file is specific to parsing
39 * MaxMind's various GeoIP databases, and much of it was obviously created by
40 * examining the code of (and copying the constants from) MaxMind's own
41 * libGeoIP, which is licensed under the LGPL.
42 * The code in this file is licensed under the GPLv3, which is compatible,
43 * but in any case it's mostly just constants that were copied.
44 ****************************************************************************/
45
46 #define COUNTRY_BEGIN 16776960
47 #define LARGE_COUNTRY_BEGIN 16515072
48 #define STATE_BEGIN_REV1 16000000
49 #define US_OFFSET 1
50 #define CANADA_OFFSET 677
51 #define WORLD_OFFSET 1353
52 #define FIPS_RANGE 360
53 #define STRUCTURE_INFO_MAX_SIZE 20
54 #define GEOIP_COUNTRY_EDITION 1
55 #define GEOIP_CITY_EDITION_REV1 2
56 #define GEOIP_REGION_EDITION_REV1 3
57 #define GEOIP_CITY_EDITION_REV0 6
58 #define GEOIP_COUNTRY_EDITION_V6 12
59 #define GEOIP_LARGE_COUNTRY_EDITION 17
60 #define GEOIP_LARGE_COUNTRY_EDITION_V6 18
61 #define GEOIP_CITY_EDITION_REV1_V6 30
62 #define GEOIP_CITY_EDITION_REV0_V6 31
63
64 #define NUM_COUNTRIES 256
65 #define DEF_COUNTRYID 255 // if overflow
66
67 static const char GeoIP_country_continent[NUM_COUNTRIES][3] = { "--",
68 "AS","EU","EU","AS","AS","NA","NA","EU","AS","NA",
69 "AF","AN","SA","OC","EU","OC","NA","AS","EU","NA",
70 "AS","EU","AF","EU","AS","AF","AF","NA","AS","SA",
71 "SA","NA","AS","AN","AF","EU","NA","NA","AS","AF",
72 "AF","AF","EU","AF","OC","SA","AF","AS","SA","NA",
73 "NA","AF","AS","AS","EU","EU","AF","EU","NA","NA",
74 "AF","SA","EU","AF","AF","AF","EU","AF","EU","OC",
75 "SA","OC","EU","EU","NA","AF","EU","NA","AS","SA",
76 "AF","EU","NA","AF","AF","NA","AF","EU","AN","NA",
77 "OC","AF","SA","AS","AN","NA","EU","NA","EU","AS",
78 "EU","AS","AS","AS","AS","AS","EU","EU","NA","AS",
79 "AS","AF","AS","AS","OC","AF","NA","AS","AS","AS",
80 "NA","AS","AS","AS","NA","EU","AS","AF","AF","EU",
81 "EU","EU","AF","AF","EU","EU","AF","OC","EU","AF",
82 "AS","AS","AS","OC","NA","AF","NA","EU","AF","AS",
83 "AF","NA","AS","AF","AF","OC","AF","OC","AF","NA",
84 "EU","EU","AS","OC","OC","OC","AS","NA","SA","OC",
85 "OC","AS","AS","EU","NA","OC","NA","AS","EU","OC",
86 "SA","AS","AF","EU","EU","AF","AS","OC","AF","AF",
87 "EU","AS","AF","EU","EU","EU","AF","EU","AF","AF",
88 "SA","AF","NA","AS","AF","NA","AF","AN","AF","AS",
89 "AS","OC","AS","AF","OC","AS","EU","NA","OC","AS",
90 "AF","EU","AF","OC","NA","SA","AS","EU","NA","SA",
91 "NA","NA","AS","OC","OC","OC","AS","AF","EU","AF",
92 "AF","EU","AF","--","--","--","EU","EU","EU","EU",
93 "NA","NA","NA","AF","--"
94 };
95
96 static const char GeoIP_country_code[NUM_COUNTRIES][3] = { "--",
97 "AP","EU","AD","AE","AF","AG","AI","AL","AM","CW",
98 "AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB",
99 "BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO",
100 "BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD",
101 "CF","CG","CH","CI","CK","CL","CM","CN","CO","CR",
102 "CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO",
103 "DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ",
104 "FK","FM","FO","FR","SX","GA","GB","GD","GE","GF",
105 "GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT",
106 "GU","GW","GY","HK","HM","HN","HR","HT","HU","ID",
107 "IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO",
108 "JP","KE","KG","KH","KI","KM","KN","KP","KR","KW",
109 "KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT",
110 "LU","LV","LY","MA","MC","MD","MG","MH","MK","ML",
111 "MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV",
112 "MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI",
113 "NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF",
114 "PG","PH","PK","PL","PM","PN","PR","PS","PT","PW",
115 "PY","QA","RE","RO","RU","RW","SA","SB","SC","SD",
116 "SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO",
117 "SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH",
118 "TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW",
119 "TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE",
120 "VG","VI","VN","VU","WF","WS","YE","YT","RS","ZA",
121 "ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE",
122 "BL","MF","BQ","SS","O1"
123 };
124
125 /*******************************
126 * End copied MaxMind constants
127 *******************************/
128
129 // this one's just for map validation
130 #define NUM_CONTINENTS 8
131 static const char continent_list[NUM_CONTINENTS][3] = {
132 "--", "AS", "AF", "OC", "EU", "NA", "SA", "AN"
133 };
134
135 typedef struct {
136 unsigned offset;
137 uint32_t dclist;
138 } offset_cache_item_t;
139 #define OFFSET_CACHE_SIZE 129113 // prime
140
141 struct _geoip_db;
142 typedef struct _geoip_db geoip_db_t;
143
144 typedef uint32_t (*dclist_get_func_t)(const geoip_db_t* db, const unsigned offset);
145
146 struct _geoip_db {
147 const char* pathname;
148 const char* map_name;
149 gdnsd_fmap_t* fmap;
150 const fips_t* fips;
151 const dcmap_t* dcmap;
152 dclists_t* dclists;
153 dclist_get_func_t dclist_get_func;
154 const uint8_t* data;
155 unsigned base;
156 unsigned size;
157 int type;
158 gdgeoip_v4o_t v4o_flag;
159 bool ipv6;
160 bool city_auto_mode;
161 bool city_no_region;
162 offset_cache_item_t *offset_cache[OFFSET_CACHE_SIZE];
163 };
164
validate_country_code(const char * cc,const char * map_name)165 void validate_country_code(const char* cc, const char* map_name) {
166 for(unsigned i = 0; i < NUM_COUNTRIES; i++)
167 if( !((cc[0] ^ GeoIP_country_code[i][0]) & 0xDF)
168 && !((cc[1] ^ GeoIP_country_code[i][1]) & 0xDF)
169 && !cc[2])
170 return;
171 log_fatal("plugin_geoip: map '%s': Country code '%s' is illegal", map_name, cc);
172 }
173
validate_continent_code(const char * cc,const char * map_name)174 void validate_continent_code(const char* cc, const char* map_name) {
175 for(unsigned i = 0; i < NUM_CONTINENTS; i++)
176 if( !((cc[0] ^ continent_list[i][0]) & 0xDF)
177 && !((cc[1] ^ continent_list[i][1]) & 0xDF)
178 && !cc[2])
179 return;
180 log_fatal("plugin_geoip: map '%s': Continent code '%s' is illegal", map_name, cc);
181 }
182
183 F_NONNULL F_PURE
country_get_dclist(const geoip_db_t * db,const unsigned offset)184 static uint32_t country_get_dclist(const geoip_db_t* db, const unsigned offset) {
185 dmn_assert(offset >= db->base);
186
187 unsigned rv = 0;
188 if(db->dcmap) {
189 unsigned ccid = offset - db->base;
190 if(ccid >= NUM_COUNTRIES)
191 ccid = DEF_COUNTRYID;
192 char locstr[7];
193
194 locstr[0] = GeoIP_country_continent[ccid][0];
195 locstr[1] = GeoIP_country_continent[ccid][1];
196 locstr[2] = '\0';
197 locstr[3] = GeoIP_country_code[ccid][0];
198 locstr[4] = GeoIP_country_code[ccid][1];
199 locstr[5] = '\0';
200 locstr[6] = '\0';
201
202 rv = dcmap_lookup_loc(db->dcmap, locstr);
203 }
204
205 return rv;
206 }
207
208 F_NONNULL
region_get_dclist(const geoip_db_t * db,const unsigned offset)209 static uint32_t region_get_dclist(const geoip_db_t* db, const unsigned offset) {
210 dmn_assert(offset >= db->base);
211
212 unsigned rv = 0;
213 if(db->dcmap) {
214 const unsigned ccid = offset - db->base;
215 char locstr[10];
216
217 if (ccid < US_OFFSET) {
218 locstr[0] = '-';
219 locstr[1] = '-';
220 locstr[2] = '\0';
221 locstr[3] = '-';
222 locstr[4] = '-';
223 locstr[5] = '\0';
224 locstr[6] = '\0';
225 }
226 else if (ccid < CANADA_OFFSET) {
227 locstr[0] = 'N';
228 locstr[1] = 'A';
229 locstr[2] = '\0';
230 locstr[3] = 'U';
231 locstr[4] = 'S';
232 locstr[5] = '\0';
233 locstr[6] = (char) ((ccid - US_OFFSET) / 26 + 65);
234 locstr[7] = (char) ((ccid - US_OFFSET) % 26 + 65);
235 locstr[8] = '\0';
236 locstr[9] = '\0';
237 }
238 else if (ccid < WORLD_OFFSET) {
239 locstr[0] = 'N';
240 locstr[1] = 'A';
241 locstr[2] = '\0';
242 locstr[3] = 'C';
243 locstr[4] = 'A';
244 locstr[5] = '\0';
245 locstr[6] = (char) ((ccid - CANADA_OFFSET) / 26 + 65);
246 locstr[7] = (char) ((ccid - CANADA_OFFSET) % 26 + 65);
247 locstr[8] = '\0';
248 locstr[9] = '\0';
249 }
250 else {
251 const unsigned ccnum = (ccid - WORLD_OFFSET) / FIPS_RANGE;
252 locstr[0] = GeoIP_country_continent[ccnum][0];
253 locstr[1] = GeoIP_country_continent[ccnum][1];
254 locstr[2] = '\0';
255 locstr[3] = GeoIP_country_code[ccnum][0];
256 locstr[4] = GeoIP_country_code[ccnum][1];
257 locstr[5] = '\0';
258 locstr[6] = '\0';
259 }
260
261 rv = dcmap_lookup_loc(db->dcmap, locstr);
262 }
263
264 return rv;
265 }
266
267 F_NONNULL
city_get_dclist(const geoip_db_t * db,unsigned offs)268 static uint32_t city_get_dclist(const geoip_db_t* db, unsigned offs) {
269 dmn_assert(offs >= db->base);
270
271 char locstr[256];
272 unsigned raw_lat = 0;
273 unsigned raw_lon = 0;
274
275 if(!db->city_auto_mode && !db->dcmap)
276 return 0;
277
278 // Not found in DB
279 if(offs == db->base) {
280 if(db->dcmap) {
281 locstr[0] = '-';
282 locstr[1] = '-';
283 locstr[2] = '\0';
284 locstr[3] = '-';
285 locstr[4] = '-';
286 locstr[5] = '\0';
287 locstr[6] = '\0';
288 }
289 // 1800000 == 0.0 when raw is converted to floating-point degrees
290 raw_lat = 1800000;
291 raw_lon = 1800000;
292 }
293 else {
294 offs += 5 * db->base;
295 const uint8_t* rec = &db->data[offs];
296
297 if(db->dcmap) {
298 locstr[0] = GeoIP_country_continent[rec[0]][0];
299 locstr[1] = GeoIP_country_continent[rec[0]][1];
300 locstr[2] = '\0';
301 locstr[3] = GeoIP_country_code[rec[0]][0];
302 locstr[4] = GeoIP_country_code[rec[0]][1];
303 locstr[5] = '\0';
304 }
305
306 unsigned loc_pos = 6;
307 rec++;
308
309 // Get ptr to region_name from db, get length, skip past it in db
310 const char* region_name = (const char*)rec;
311 unsigned region_len = strlen(region_name);
312 rec += region_len;
313 rec++;
314
315 // If we want to use region-level info...
316 if(db->dcmap && !db->city_no_region) {
317 // Check for FIPS 10-4 conversion, replacing
318 // region_name/region_len if so.
319 if(region_len == 2 && db->fips) {
320 const uint32_t key = ((unsigned)locstr[3])
321 + ((unsigned)locstr[4] << 8U)
322 + ((unsigned)region_name[0] << 16U)
323 + ((unsigned)region_name[1] << 24U);
324 const char* rname = fips_lookup(db->fips, key);
325 if(rname) {
326 region_name = rname;
327 region_len = strlen(region_name);
328 }
329 }
330
331 if(!region_len || !*region_name || region_len > 120U) {
332 // Handle oversize and empty cases as "--"
333 if(region_len > 120U)
334 log_err("plugin_geoip: Bug: GeoIP City region name much longer than expected: %u '%s'", region_len, rec);
335 locstr[loc_pos++] = '-';
336 locstr[loc_pos++] = '-';
337 }
338 else {
339 memcpy(&locstr[loc_pos], region_name, region_len);
340 loc_pos += region_len;
341 }
342 locstr[loc_pos++] = '\0';
343 }
344
345 const char* city_name = (const char*)rec;
346 const unsigned city_len = strlen(city_name);
347 rec += city_len;
348 rec++;
349
350 if(db->dcmap) {
351 if(city_len > 120U) {
352 log_err("plugin_geoip: Bug: GeoIP City city name much longer than expected: %u '%s'", city_len, rec);
353 }
354 else if(city_len) {
355 memcpy(&locstr[loc_pos], city_name, city_len);
356 loc_pos += city_len;
357 locstr[loc_pos++] = '\0';
358 }
359 }
360
361 // skip past postal code
362 rec += strlen((const char*)rec);
363 rec++;
364
365 for(unsigned j = 0; j < 3; ++j)
366 raw_lat += ((unsigned)rec[j] << (j * 8U));
367 rec += 3;
368
369 for(unsigned j = 0; j < 3; ++j)
370 raw_lon += ((unsigned)rec[j] << (j * 8U));
371
372 if(db->dcmap)
373 locstr[loc_pos] = '\0';
374 }
375
376 uint32_t dclist = db->dcmap
377 ? dcmap_lookup_loc(db->dcmap, locstr)
378 : DCLIST_AUTO;
379
380 if(dclist == DCLIST_AUTO) {
381 dmn_assert(db->city_auto_mode);
382
383 // default for 0/0 coords
384 if(raw_lat == 1800000 && raw_lon == 1800000) {
385 dclist = 0;
386 }
387 else {
388 const double lat_deg = (raw_lat - 1800000.0) * 0.0001;
389 const double lon_deg = (raw_lon - 1800000.0) * 0.0001;
390 dclist = dclists_city_auto_map(db->dclists, db->map_name, lat_deg, lon_deg);
391 dmn_assert(dclist != DCLIST_AUTO);
392 dmn_assert(dclist <= DCLIST_MAX);
393 }
394 }
395
396 dmn_assert(dclist != DCLIST_AUTO);
397 dmn_assert(dclist <= DCLIST_MAX);
398 return dclist;
399 }
400
401 F_NONNULL
get_dclist_cached(geoip_db_t * db,const unsigned offset)402 static uint32_t get_dclist_cached(geoip_db_t* db, const unsigned offset) {
403 unsigned bucket_size = 0;
404 const unsigned ndx = offset % OFFSET_CACHE_SIZE;
405
406 if(db->offset_cache[ndx]) {
407 for(bucket_size = 0; db->offset_cache[ndx][bucket_size].offset; bucket_size++)
408 if(db->offset_cache[ndx][bucket_size].offset == offset)
409 return db->offset_cache[ndx][bucket_size].dclist;
410 }
411
412 uint32_t dclist = db->dclist_get_func(db, offset);
413 db->offset_cache[ndx] = xrealloc(db->offset_cache[ndx], sizeof(offset_cache_item_t) * (bucket_size + 2));
414 dmn_assert(db->offset_cache[ndx]);
415 db->offset_cache[ndx][bucket_size].offset = offset;
416 db->offset_cache[ndx][bucket_size].dclist = dclist;
417 db->offset_cache[ndx][bucket_size + 1].offset = 0;
418 dmn_assert(dclist <= DCLIST_MAX); // auto not allowed here, should have been resolved earlier
419 return dclist;
420 }
421
422 F_NONNULL
list_xlate_recurse(geoip_db_t * db,nlist_t * nl,struct in6_addr ip,const unsigned depth,const unsigned db_off)423 static bool list_xlate_recurse(geoip_db_t* db, nlist_t* nl, struct in6_addr ip, const unsigned depth, const unsigned db_off) {
424 dmn_assert(depth < 129);
425
426 bool rv = false;
427
428 do {
429 if(!depth || ((3 * 2 * db_off) + 6) > db->size) {
430 log_err("plugin_geoip: map '%s': Error traversing GeoIP database, corrupt?", db->map_name);
431 rv = true;
432 break;
433 }
434
435 // skip v4-like spaces as applicable...
436 if(depth == 32) {
437 if(!memcmp(ip.s6_addr, start_v4compat, 12) && db->v4o_flag == V4O_PRIMARY)
438 break;
439 else if(!memcmp(ip.s6_addr, start_v4mapped, 12))
440 break;
441 else if(!memcmp(ip.s6_addr, start_siit, 12))
442 break;
443 else if(!memcmp(ip.s6_addr, start_wkp, 12))
444 break;
445 }
446 else if(depth == 96 && !memcmp(ip.s6_addr, start_teredo, 4)) {
447 break;
448 }
449 else if(depth == 112 && !memcmp(ip.s6_addr, start_6to4, 2)) {
450 break;
451 }
452
453 const unsigned char *db_buf = db->data + 3 * 2 * db_off;
454 const unsigned db_zero_off = (unsigned)db_buf[0]
455 + ((unsigned)db_buf[1] << 8)
456 + ((unsigned)db_buf[2] << 16);
457 const unsigned db_one_off = (unsigned)db_buf[3]
458 + ((unsigned)db_buf[4] << 8)
459 + ((unsigned)db_buf[5] << 16);
460
461 const unsigned next_depth = depth - 1U;
462 const unsigned mask = 128U - next_depth;
463
464 if(db_zero_off >= db->base) {
465 nlist_append(nl, ip.s6_addr, mask, get_dclist_cached(db, db_zero_off));
466 }
467 else if(list_xlate_recurse(db, nl, ip, next_depth, db_zero_off)) {
468 rv = true;
469 break;
470 }
471
472 SETBIT_v6(ip.s6_addr, mask - 1);
473
474 if(db_one_off >= db->base) {
475 nlist_append(nl, ip.s6_addr, mask, get_dclist_cached(db, db_one_off));
476 }
477 else if(list_xlate_recurse(db, nl, ip, next_depth, db_one_off)) {
478 rv = true;
479 break;
480 }
481 } while(0);
482
483 return rv;
484 }
485
486 F_NONNULL
geoip_db_close(geoip_db_t * db)487 static bool geoip_db_close(geoip_db_t* db) {
488 bool rv = false;
489 if(db->fmap)
490 rv = gdnsd_fmap_delete(db->fmap);
491 for (unsigned i = 0; i < OFFSET_CACHE_SIZE; i++)
492 free(db->offset_cache[i]);
493 free(db);
494 return rv;
495 }
496
497 F_NONNULLX(1,2,3)
geoip_db_open(const char * pathname,const char * map_name,dclists_t * dclists,const dcmap_t * dcmap,const fips_t * fips,const gdgeoip_v4o_t v4o_flag,const bool city_auto_mode,const bool city_no_region)498 static geoip_db_t* geoip_db_open(const char* pathname, const char* map_name, dclists_t* dclists, const dcmap_t* dcmap, const fips_t* fips, const gdgeoip_v4o_t v4o_flag, const bool city_auto_mode, const bool city_no_region) {
499 geoip_db_t* db = xcalloc(1, sizeof(geoip_db_t));
500 db->pathname = pathname;
501 db->map_name = map_name;
502 db->dclists = dclists;
503 db->dcmap = dcmap;
504 db->v4o_flag = v4o_flag;
505 db->city_auto_mode = city_auto_mode;
506 db->city_no_region = city_no_region;
507
508 db->fmap = gdnsd_fmap_new(pathname, false);
509 if(!db->fmap) {
510 log_err("plugin_geoip: map '%s': Cannot load '%s'", map_name, pathname);
511 free(db);
512 return NULL;
513 }
514
515 db->size = gdnsd_fmap_get_len(db->fmap);
516 db->data = gdnsd_fmap_get_buf(db->fmap);
517
518 // 9 bytes would be a single record splitting the IPv4
519 // space into 0.0.0.0/1 and 128.0.0.0/1, each mapped
520 // to a single countryid, plus the requisite 0xFFFFFF
521 // end marker.
522 if(db->size < 9) {
523 log_err("plugin_geoip: map '%s': GeoIP database '%s' too small", map_name, pathname);
524 geoip_db_close(db);
525 return NULL;
526 }
527
528 /* This GeoIP structure info stuff is confusing...
529 * Apparently the first structure info record is the final
530 * 3 bytes of the file. If that's 0xFFFFFF, we're done,
531 * and it's a plain country database.
532 * If those 3 bytes aren't 0xFFFFFF, then we step back by
533 * one byte and try again. From here on when we get
534 * our match on the first 3 bytes being 0xFFFFFF, the
535 * 4th byte is the database type.
536 */
537 db->type = GEOIP_COUNTRY_EDITION;
538 unsigned offset = db->size - 3U;
539 for(unsigned i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) {
540 if(db->data[offset] == 255 && db->data[offset + 1] == 255 && db->data[offset + 2] == 255) {
541 if(i)
542 db->type = db->data[offset + 3];
543 break;
544 }
545 // if offset reaches zero, the database is borked as far as we're concerned
546 if(!offset) {
547 log_err("plugin_geoip: map '%s': Could not find database info structure in '%s'", map_name, pathname);
548 geoip_db_close(db);
549 return NULL;
550 }
551 offset--;
552 }
553
554 if(city_auto_mode) {
555 switch(db->type) {
556 case GEOIP_CITY_EDITION_REV0_V6:
557 case GEOIP_CITY_EDITION_REV1_V6:
558 case GEOIP_CITY_EDITION_REV0:
559 case GEOIP_CITY_EDITION_REV1:
560 break;
561 default:
562 log_err("plugin_geoip: map '%s': GeoIP DB '%s' is not a City-level database and this map uses auto_dc_coords", map_name, db->pathname);
563 geoip_db_close(db);
564 return NULL;
565 }
566 }
567
568 switch(db->type) {
569 case GEOIP_COUNTRY_EDITION_V6:
570 db->ipv6 = true;
571 // fall-through intentional
572 case GEOIP_COUNTRY_EDITION:
573 db->base = COUNTRY_BEGIN;
574 db->dclist_get_func = country_get_dclist;
575 break;
576
577 case GEOIP_LARGE_COUNTRY_EDITION_V6:
578 db->ipv6 = true;
579 // fall-through intentional
580 case GEOIP_LARGE_COUNTRY_EDITION:
581 db->base = LARGE_COUNTRY_BEGIN;
582 db->dclist_get_func = country_get_dclist;
583 break;
584
585 case GEOIP_REGION_EDITION_REV1:
586 db->base = STATE_BEGIN_REV1;
587 db->dclist_get_func = region_get_dclist;
588 break;
589
590 case GEOIP_CITY_EDITION_REV0_V6:
591 case GEOIP_CITY_EDITION_REV1_V6:
592 db->ipv6 = true;
593 // fall-through intentional
594 case GEOIP_CITY_EDITION_REV0:
595 case GEOIP_CITY_EDITION_REV1:
596 offset += 4U;
597 for(unsigned i = 0; i < 3; i++)
598 db->base += ((unsigned)db->data[offset + i] << (i * 8U));
599 if(fips)
600 db->fips = fips;
601 db->dclist_get_func = city_get_dclist;
602 break;
603
604 default:
605 log_err("plugin_geoip: map '%s': GeoIP DB '%s': Unrecognized DB type %i", map_name, db->pathname, db->type);
606 geoip_db_close(db);
607 return NULL;
608 }
609
610 if((v4o_flag == V4O_PRIMARY) && !db->ipv6) {
611 log_err("plugin_geoip: map '%s': Primary GeoIP DB '%s' is not an IPv6 database and this map uses geoip_v4_overlay", map_name, db->pathname);
612 geoip_db_close(db);
613 return NULL;
614 }
615 else if((v4o_flag == V4O_SECONDARY) && db->ipv6) {
616 log_err("plugin_geoip: map '%s': geoip_v4_overlay database '%s' is not an IPv4 database", map_name, db->pathname);
617 geoip_db_close(db);
618 return NULL;
619 }
620
621 return db;
622 }
623
gdgeoip_make_list(const char * pathname,const char * map_name,dclists_t * dclists,const dcmap_t * dcmap,const fips_t * fips,const gdgeoip_v4o_t v4o_flag,const bool city_auto_mode,const bool city_no_region)624 nlist_t* gdgeoip_make_list(const char* pathname, const char* map_name, dclists_t* dclists, const dcmap_t* dcmap, const fips_t* fips, const gdgeoip_v4o_t v4o_flag, const bool city_auto_mode, const bool city_no_region) {
625 log_info("plugin_geoip: map '%s': Processing GeoIP database '%s'", map_name, pathname);
626
627 nlist_t* nl = NULL;
628
629 geoip_db_t* geodb = geoip_db_open(pathname, map_name, dclists, dcmap, fips, v4o_flag, city_auto_mode, city_no_region);
630 if(geodb) {
631 nl = nlist_new(map_name, true);
632
633 const unsigned start_depth = geodb->ipv6 ? 128 : 32;
634 const bool rec_rv = list_xlate_recurse(geodb, nl, ip6_zero, start_depth, 0);
635 const bool close_rv = geoip_db_close(geodb);
636
637 if(rec_rv || close_rv) {
638 nlist_destroy(nl);
639 nl = NULL;
640 }
641 else {
642 nlist_finish(nl);
643 }
644 }
645
646 return nl;
647 }
648