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