]> granicus.if.org Git - pdns/commitdiff
geoipbackend: Move GeoIP handling to separate class
authorAki Tuomi <cmouse@cmouse.fi>
Sat, 30 Dec 2017 11:24:29 +0000 (13:24 +0200)
committerAki Tuomi <cmouse@cmouse.fi>
Wed, 14 Feb 2018 07:15:53 +0000 (09:15 +0200)
Makes it easier to add new interfaces

modules/geoipbackend/Makefile.am
modules/geoipbackend/OBJECTFILES
modules/geoipbackend/geoipbackend.cc
modules/geoipbackend/geoipbackend.hh
modules/geoipbackend/geoipinterface-dat.cc [new file with mode: 0644]
modules/geoipbackend/geoipinterface.cc [new file with mode: 0644]
modules/geoipbackend/geoipinterface.hh [new file with mode: 0644]

index b0d0ee924cd3183025a20e5c04554da7a680720e..7aad6f6f673d159964419434bd0ee2f40bdca0a1 100644 (file)
@@ -4,6 +4,8 @@ EXTRA_DIST = OBJECTFILES OBJECTLIBS
 
 pkglib_LTLIBRARIES = libgeoipbackend.la
 
-libgeoipbackend_la_SOURCES = geoipbackend.cc geoipbackend.hh
+libgeoipbackend_la_SOURCES = geoipbackend.cc geoipbackend.hh \
+       geoipinterface.cc geoipinterface.hh \
+       geoipinterface-dat.cc
 libgeoipbackend_la_LDFLAGS = -module -avoid-version
 libgeoipbackend_la_LIBADD = $(YAML_LIBS) $(GEOIP_LIBS)
index 7173c166cbacfb5f6412dcf6fb09a10ee9474931..b2245db984a458e725f53c29370eb56f5b7b68a0 100644 (file)
@@ -1 +1 @@
-geoipbackend.lo
+geoipbackend.lo geoipinterface-dat.lo  geoipinterface.lo
index e767836d2abf818cd58fc16b2aa72c746516ad66..6ed40cd627b364071af04dae294ca9a7becb98f4 100644 (file)
@@ -23,6 +23,7 @@
 #include "config.h"
 #endif
 #include "geoipbackend.hh"
+#include "geoipinterface.hh"
 #include "pdns/dns_random.hh"
 #include <sstream>
 #include <regex.h>
@@ -49,14 +50,6 @@ public:
 static vector<GeoIPDomain> s_domains;
 static int s_rc = 0; // refcount
 
-struct geoip_deleter {
-  void operator()(GeoIP* ptr) {
-    if (ptr) GeoIP_delete(ptr);
-  };
-};
-
-static vector<GeoIPBackend::geoip_file_t> s_geoip_files;
-
 static string GeoIP_WEEKDAYS[] = { "mon", "tue", "wed", "thu", "fri", "sat", "sun" };
 static string GeoIP_MONTHS[] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" };
 
@@ -87,38 +80,21 @@ GeoIPBackend::GeoIPBackend(const string& suffix) {
   s_rc++;
 }
 
+static vector<std::unique_ptr<GeoIPInterface> > s_geoip_files;
+
 void GeoIPBackend::initialize() {
   YAML::Node config;
   vector<GeoIPDomain> tmp_domains;
 
   string modeStr = getArg("database-cache");
-  int flags;
-  if (modeStr == "standard")
-    flags = GEOIP_STANDARD;
-  else if (modeStr == "memory")
-    flags = GEOIP_MEMORY_CACHE;
-  else if (modeStr == "index")
-    flags = GEOIP_INDEX_CACHE;
-#ifdef HAVE_MMAP
-  else if (modeStr == "mmap")
-    flags = GEOIP_MMAP_CACHE;
-#endif
-  else
-    throw PDNSException("Invalid cache mode " + modeStr + " for GeoIP backend");
-
   s_geoip_files.clear(); // reset pointers
 
   if (getArg("database-files").empty() == false) {
     vector<string> files;
     stringtok(files, getArg("database-files"), " ,\t\r\n");
     for(auto const& file: files) {
-      GeoIP *fptr;
-      int mode;
-      fptr = GeoIP_open(file.c_str(), flags);
-      if (!fptr)
-        throw PDNSException("Cannot open GeoIP database " + file);
-      mode = GeoIP_database_edition(fptr);
-      s_geoip_files.emplace_back(geoip_file_t(mode, unique_ptr<GeoIP,geoip_deleter>(fptr)));
+      const string& fileStr = string("dat:") + file + string(";mode=") + modeStr;
+      s_geoip_files.push_back(GeoIPInterface::makeInterface(fileStr));
     }
   }
 
@@ -420,274 +396,7 @@ bool GeoIPBackend::get(DNSResourceRecord &r) {
   return true;
 }
 
-bool GeoIPBackend::queryCountry(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi) {
-  if (gi.first == GEOIP_COUNTRY_EDITION ||
-      gi.first == GEOIP_LARGE_COUNTRY_EDITION) {
-    ret = GeoIP_code3_by_id(GeoIP_id_by_addr_gl(gi.second.get(), ip.c_str(), gl));
-    return true;
-  } else if (gi.first == GEOIP_REGION_EDITION_REV0 ||
-             gi.first == GEOIP_REGION_EDITION_REV1) {
-    GeoIPRegion* gir = GeoIP_region_by_addr_gl(gi.second.get(), ip.c_str(), gl);
-    if (gir) {
-      ret = GeoIP_code3_by_id(GeoIP_id_by_code(gir->country_code));
-      return true;
-    }
-  } else if (gi.first == GEOIP_CITY_EDITION_REV0 ||
-             gi.first == GEOIP_CITY_EDITION_REV1) {
-    GeoIPRecord *gir = GeoIP_record_by_addr(gi.second.get(), ip.c_str());
-    if (gir) {
-      ret = gir->country_code3;
-      gl->netmask = gir->netmask;
-      return true;
-    }
-  }
-  return false;
-}
-
-bool GeoIPBackend::queryCountryV6(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi) {
-  if (gi.first == GEOIP_COUNTRY_EDITION_V6 ||
-      gi.first == GEOIP_LARGE_COUNTRY_EDITION_V6) {
-    ret = GeoIP_code3_by_id(GeoIP_id_by_addr_v6_gl(gi.second.get(), ip.c_str(), gl));
-    return true;
-  } else if (gi.first == GEOIP_REGION_EDITION_REV0 ||
-             gi.first == GEOIP_REGION_EDITION_REV1) {
-    GeoIPRegion* gir = GeoIP_region_by_addr_v6_gl(gi.second.get(), ip.c_str(), gl);
-    if (gir) {
-      ret = GeoIP_code3_by_id(GeoIP_id_by_code(gir->country_code));
-      return true;
-    }
-  } else if (gi.first == GEOIP_CITY_EDITION_REV0_V6 ||
-             gi.first == GEOIP_CITY_EDITION_REV1_V6) {
-    GeoIPRecord *gir = GeoIP_record_by_addr_v6(gi.second.get(), ip.c_str());
-    if (gir) {
-      ret = gir->country_code3;
-      gl->netmask = gir->netmask;
-      return true;
-    }
-  }
-  return false;
-}
-
-bool GeoIPBackend::queryCountry2(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi) {
-  if (gi.first == GEOIP_COUNTRY_EDITION ||
-      gi.first == GEOIP_LARGE_COUNTRY_EDITION) {
-    ret = GeoIP_code_by_id(GeoIP_id_by_addr_gl(gi.second.get(), ip.c_str(), gl));
-    return true;
-  } else if (gi.first == GEOIP_REGION_EDITION_REV0 ||
-             gi.first == GEOIP_REGION_EDITION_REV1) {
-    GeoIPRegion* gir = GeoIP_region_by_addr_gl(gi.second.get(), ip.c_str(), gl);
-    if (gir) {
-      ret = GeoIP_code_by_id(GeoIP_id_by_code(gir->country_code));
-      return true;
-    }
-  } else if (gi.first == GEOIP_CITY_EDITION_REV0 ||
-             gi.first == GEOIP_CITY_EDITION_REV1) {
-    GeoIPRecord *gir = GeoIP_record_by_addr(gi.second.get(), ip.c_str());
-    if (gir) {
-      ret = gir->country_code;
-      gl->netmask = gir->netmask;
-      return true;
-    }
-  }
-  return false;
-}
-
-bool GeoIPBackend::queryCountry2V6(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi) {
-  if (gi.first == GEOIP_COUNTRY_EDITION_V6 ||
-      gi.first == GEOIP_LARGE_COUNTRY_EDITION_V6) {
-    ret = GeoIP_code_by_id(GeoIP_id_by_addr_v6_gl(gi.second.get(), ip.c_str(), gl));
-    return true;
-  } else if (gi.first == GEOIP_REGION_EDITION_REV0 ||
-             gi.first == GEOIP_REGION_EDITION_REV1) {
-    GeoIPRegion* gir = GeoIP_region_by_addr_v6_gl(gi.second.get(), ip.c_str(), gl);
-    if (gir) {
-      ret = GeoIP_code_by_id(GeoIP_id_by_code(gir->country_code));
-      return true;
-    }
-  } else if (gi.first == GEOIP_CITY_EDITION_REV0_V6 ||
-             gi.first == GEOIP_CITY_EDITION_REV1_V6) {
-    GeoIPRecord *gir = GeoIP_record_by_addr_v6(gi.second.get(), ip.c_str());
-    if (gir) {
-      ret = gir->country_code;
-      gl->netmask = gir->netmask;
-      return true;
-    }
-  }
-  return false;
-}
-
-bool GeoIPBackend::queryContinent(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi) {
-  if (gi.first == GEOIP_COUNTRY_EDITION ||
-      gi.first == GEOIP_LARGE_COUNTRY_EDITION) {
-    ret = GeoIP_continent_by_id(GeoIP_id_by_addr_gl(gi.second.get(), ip.c_str(), gl));
-    return true;
-  } else if (gi.first == GEOIP_REGION_EDITION_REV0 ||
-             gi.first == GEOIP_REGION_EDITION_REV1) {
-    GeoIPRegion* gir = GeoIP_region_by_addr_gl(gi.second.get(), ip.c_str(), gl);
-    if (gir) {
-      ret = GeoIP_continent_by_id(GeoIP_id_by_code(gir->country_code));
-      return true;
-    }
-  } else if (gi.first == GEOIP_CITY_EDITION_REV0 ||
-             gi.first == GEOIP_CITY_EDITION_REV1) {
-    GeoIPRecord *gir = GeoIP_record_by_addr(gi.second.get(), ip.c_str());
-    if (gir) {
-      ret =  ret = GeoIP_continent_by_id(GeoIP_id_by_code(gir->country_code));
-      gl->netmask = gir->netmask;
-      return true;
-    }
-  }
-  return false;
-}
-
-bool GeoIPBackend::queryContinentV6(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi) {
-  if (gi.first == GEOIP_COUNTRY_EDITION_V6 ||
-      gi.first == GEOIP_LARGE_COUNTRY_EDITION_V6) {
-    ret = GeoIP_continent_by_id(GeoIP_id_by_addr_v6_gl(gi.second.get(), ip.c_str(), gl));
-    return true;
-  } else if (gi.first == GEOIP_REGION_EDITION_REV0 ||
-             gi.first == GEOIP_REGION_EDITION_REV1) {
-    GeoIPRegion* gir = GeoIP_region_by_addr_v6_gl(gi.second.get(), ip.c_str(), gl);
-    if (gir) {
-      ret = GeoIP_continent_by_id(GeoIP_id_by_code(gir->country_code));
-      return true;
-    }
-  } else if (gi.first == GEOIP_CITY_EDITION_REV0_V6 ||
-             gi.first == GEOIP_CITY_EDITION_REV1_V6) {
-    GeoIPRecord *gir = GeoIP_record_by_addr_v6(gi.second.get(), ip.c_str());
-    if (gir) {
-      ret = GeoIP_continent_by_id(GeoIP_id_by_code(gir->country_code));
-      gl->netmask = gir->netmask;
-      return true;
-    }
-  }
-  return false;
-}
-
-bool GeoIPBackend::queryName(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi) {
-  if (gi.first == GEOIP_ISP_EDITION ||
-      gi.first == GEOIP_ORG_EDITION) {
-    string val = valueOrEmpty<char*,string>(GeoIP_name_by_addr_gl(gi.second.get(), ip.c_str(), gl));
-    if (!val.empty()) {
-      // reduce space to dash
-      ret = boost::replace_all_copy(val, " ", "-");
-      return true;
-    }
-  }
-  return false;
-}
-
-bool GeoIPBackend::queryNameV6(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi) {
-  if (gi.first == GEOIP_ISP_EDITION_V6 ||
-      gi.first == GEOIP_ORG_EDITION_V6) {
-    string val = valueOrEmpty<char*,string>(GeoIP_name_by_addr_v6_gl(gi.second.get(), ip.c_str(), gl));
-    if (!val.empty()) {
-      // reduce space to dash
-      ret = boost::replace_all_copy(val, " ", "-");
-      return true;
-    }
-  }
-  return false;
-}
-
-bool GeoIPBackend::queryASnum(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi) {
-  if (gi.first == GEOIP_ASNUM_EDITION) {
-    string val = valueOrEmpty<char*,string>(GeoIP_name_by_addr_gl(gi.second.get(), ip.c_str(), gl));
-    if (!val.empty()) {
-      vector<string> asnr;
-      stringtok(asnr, val);
-      if(asnr.size()>0) {
-        ret = asnr[0];
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-bool GeoIPBackend::queryASnumV6(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi) {
-  if (gi.first == GEOIP_ASNUM_EDITION_V6) {
-    string val = valueOrEmpty<char*,string>(GeoIP_name_by_addr_v6_gl(gi.second.get(), ip.c_str(), gl));
-    if (!val.empty()) {
-      vector<string> asnr;
-      stringtok(asnr, val);
-      if(asnr.size()>0) {
-        ret = asnr[0];
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-bool GeoIPBackend::queryRegion(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi) {
-  if (gi.first == GEOIP_REGION_EDITION_REV0 ||
-      gi.first == GEOIP_REGION_EDITION_REV1) {
-    GeoIPRegion *gir = GeoIP_region_by_addr_gl(gi.second.get(), ip.c_str(), gl);
-    if (gir) {
-      ret = valueOrEmpty<char*,string>(gir->region);
-      return true;
-    }
-  } else if (gi.first == GEOIP_CITY_EDITION_REV0 ||
-             gi.first == GEOIP_CITY_EDITION_REV1) {
-    GeoIPRecord *gir = GeoIP_record_by_addr(gi.second.get(), ip.c_str());
-    if (gir) {
-      ret = valueOrEmpty<char*,string>(gir->region);
-      gl->netmask = gir->netmask;
-      return true;
-    }
-  }
-  return false;
-}
-
-bool GeoIPBackend::queryRegionV6(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi) {
-  if (gi.first == GEOIP_REGION_EDITION_REV0 ||
-      gi.first == GEOIP_REGION_EDITION_REV1) {
-    GeoIPRegion *gir = GeoIP_region_by_addr_v6_gl(gi.second.get(), ip.c_str(), gl);
-    if (gir) {
-      ret = valueOrEmpty<char*,string>(gir->region);
-      return true;
-    }
-  } else if (gi.first == GEOIP_CITY_EDITION_REV0_V6 ||
-             gi.first == GEOIP_CITY_EDITION_REV1_V6) {
-    GeoIPRecord *gir = GeoIP_record_by_addr_v6(gi.second.get(), ip.c_str());
-    if (gir) {
-      ret = valueOrEmpty<char*,string>(gir->region);
-      gl->netmask = gir->netmask;
-      return true;
-    }
-  }
-  return false;
-}
-
-bool GeoIPBackend::queryCity(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi) {
-  if (gi.first == GEOIP_CITY_EDITION_REV0 ||
-      gi.first == GEOIP_CITY_EDITION_REV1) {
-    GeoIPRecord *gir = GeoIP_record_by_addr(gi.second.get(), ip.c_str());
-    if (gir) {
-      ret = valueOrEmpty<char*,string>(gir->city);
-      gl->netmask = gir->netmask;
-      return true;
-    }
-  }
-  return false;
-}
-
-bool GeoIPBackend::queryCityV6(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi) {
-  if (gi.first == GEOIP_CITY_EDITION_REV0_V6 ||
-      gi.first == GEOIP_CITY_EDITION_REV1_V6) {
-    GeoIPRecord *gir = GeoIP_record_by_addr_v6(gi.second.get(), ip.c_str());
-    if (gir) {
-      ret = valueOrEmpty<char*,string>(gir->city);
-      gl->netmask = gir->netmask;
-      return true;
-    }
-  }
-  return false;
-}
-
-
-string GeoIPBackend::queryGeoIP(const string &ip, bool v6, GeoIPQueryAttribute attribute, GeoIPLookup* gl) {
+string queryGeoIP(const string &ip, bool v6, GeoIPInterface::GeoIPQueryAttribute attribute, GeoIPLookup* gl) {
   string ret = "unknown";
 
   for(auto const& gi: s_geoip_files) {
@@ -695,33 +404,33 @@ string GeoIPBackend::queryGeoIP(const string &ip, bool v6, GeoIPQueryAttribute a
     bool found = false;
 
     switch(attribute) {
-    case ASn:
-      if (v6) found = queryASnumV6(val, gl, ip, gi);
-      else found = queryASnum(val, gl, ip, gi);
+    case GeoIPInterface::ASn:
+      if (v6) found = gi->queryASnumV6(val, gl, ip);
+      else found =gi->queryASnum(val, gl, ip);
       break;
-    case Name:
-      if (v6) found = queryNameV6(val, gl, ip, gi);
-      else found = queryName(val, gl, ip, gi);
+    case GeoIPInterface::Name:
+      if (v6) found = gi->queryNameV6(val, gl, ip);
+      else found = gi->queryName(val, gl, ip);
       break;
-    case Continent:
-      if (v6) found = queryContinentV6(val, gl, ip, gi);
-      else found = queryContinent(val, gl, ip, gi);
+    case GeoIPInterface::Continent:
+      if (v6) found = gi->queryContinentV6(val, gl, ip);
+      else found = gi->queryContinent(val, gl, ip);
       break;
-    case Region:
-      if (v6) found = queryRegionV6(val, gl, ip, gi);
-      else found = queryRegion(val, gl, ip, gi);
+    case GeoIPInterface::Region:
+      if (v6) found = gi->queryRegionV6(val, gl, ip);
+      else found = gi->queryRegion(val, gl, ip);
       break;
-    case Country:
-      if (v6) found = queryCountryV6(val, gl, ip, gi);
-      else found = queryCountry(val, gl, ip, gi);
+    case GeoIPInterface::Country:
+      if (v6) found = gi->queryCountryV6(val, gl, ip);
+      else found = gi->queryCountry(val, gl, ip);
       break;
-    case Country2:
-      if (v6) found = queryCountry2V6(val, gl, ip, gi);
-      else found = queryCountry2(val, gl, ip, gi);
+    case GeoIPInterface::Country2:
+      if (v6) found = gi->queryCountry2V6(val, gl, ip);
+      else found = gi->queryCountry2(val, gl, ip);
       break;
-    case City:
-      if (v6) found = queryCityV6(val, gl, ip, gi);
-      else found = queryCity(val, gl, ip, gi);
+    case GeoIPInterface::City:
+      if (v6) found = gi->queryCityV6(val, gl, ip);
+      else found = gi->queryCity(val, gl, ip);
       break;
     }
 
@@ -748,21 +457,21 @@ string GeoIPBackend::format2str(string sformat, const string& ip, bool v6, GeoIP
     int nrep=3;
     tmp_gl.netmask = 0;
     if (!sformat.compare(cur,3,"%cn")) {
-      rep = queryGeoIP(ip, v6, Continent, &tmp_gl);
+      rep = queryGeoIP(ip, v6, GeoIPInterface::Continent, &tmp_gl);
     } else if (!sformat.compare(cur,3,"%co")) {
-      rep = queryGeoIP(ip, v6, Country, &tmp_gl);
+      rep = queryGeoIP(ip, v6, GeoIPInterface::Country, &tmp_gl);
     } else if (!sformat.compare(cur,3,"%cc")) {
-      rep = queryGeoIP(ip, v6, Country2, &tmp_gl);
+      rep = queryGeoIP(ip, v6, GeoIPInterface::Country2, &tmp_gl);
     } else if (!sformat.compare(cur,3,"%af")) {
       rep = (v6?"v6":"v4");
     } else if (!sformat.compare(cur,3,"%as")) {
-      rep = queryGeoIP(ip, v6, ASn, &tmp_gl);
+      rep = queryGeoIP(ip, v6, GeoIPInterface::ASn, &tmp_gl);
     } else if (!sformat.compare(cur,3,"%re")) {
-      rep = queryGeoIP(ip, v6, Region, &tmp_gl);
+      rep = queryGeoIP(ip, v6, GeoIPInterface::Region, &tmp_gl);
     } else if (!sformat.compare(cur,3,"%na")) {
-      rep = queryGeoIP(ip, v6, Name, &tmp_gl);
+      rep = queryGeoIP(ip, v6, GeoIPInterface::Name, &tmp_gl);
     } else if (!sformat.compare(cur,3,"%ci")) {
-      rep = queryGeoIP(ip, v6, City, &tmp_gl);
+      rep = queryGeoIP(ip, v6, GeoIPInterface::City, &tmp_gl);
     } else if (!sformat.compare(cur,3,"%hh")) {
       rep = boost::str(boost::format("%02d") % gtm.tm_hour);
       tmp_gl.netmask = (v6?128:32);
index 6f0c67898053bb793e843d43f163277d7a318b67..9ebe3b5f5f4470176ba46a5769c2461c5c5dc332 100644 (file)
 #include "pdns/dnsbackend.hh"
 #include "pdns/lock.hh"
 
-struct geoip_deleter;
+class GeoIPInterface;
 
 class GeoIPDomain;
 
 class GeoIPBackend: public DNSBackend {
 public:
-  typedef pair<int,unique_ptr<GeoIP,geoip_deleter> > geoip_file_t;
   GeoIPBackend(const std::string& suffix="");
   ~GeoIPBackend();
 
@@ -68,41 +67,16 @@ public:
   bool activateDomainKey(const DNSName& name, unsigned int id) override;
   bool deactivateDomainKey(const DNSName& name, unsigned int id) override;
 
-  enum GeoIPQueryAttribute {
-    ASn,
-    City,
-    Continent,
-    Country,
-    Country2,
-    Name,
-    Region
-  };
-
 private:
   static pthread_rwlock_t s_state_lock;
 
   void initialize();
-  void ip2geo(const GeoIPDomain& dom, const string& qname, const string& ip);
-  string queryGeoIP(const string &ip, bool v6, GeoIPQueryAttribute attribute, GeoIPLookup* gl);
-  bool queryCountry(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
-  bool queryCountryV6(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
-  bool queryCountry2(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
-  bool queryCountry2V6(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
-  bool queryContinent(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
-  bool queryContinentV6(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
-  bool queryName(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
-  bool queryNameV6(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
-  bool queryASnum(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
-  bool queryASnumV6(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
-  bool queryRegion(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
-  bool queryRegionV6(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
-  bool queryCity(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
-  bool queryCityV6(string &ret, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
   string format2str(string format, const string& ip, bool v6, GeoIPLookup* gl);
-  bool d_dnssec; 
+  bool d_dnssec;
   bool hasDNSSECkey(const DNSName& name);
   bool lookup_static(const GeoIPDomain &dom, const DNSName &search, const QType &qtype, const DNSName& qdomain, const std::string &ip, GeoIPLookup &gl, bool v6);
   vector<DNSResourceRecord> d_result;
+  vector<GeoIPInterface> d_files;
 };
 
 #endif /* PDNS_GEOIPBACKEND_HH */
diff --git a/modules/geoipbackend/geoipinterface-dat.cc b/modules/geoipbackend/geoipinterface-dat.cc
new file mode 100644 (file)
index 0000000..8ced5c7
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "geoipbackend.hh"
+#include "geoipinterface.hh"
+#include "GeoIPCity.h"
+
+struct geoip_deleter {
+  void operator()(GeoIP* ptr) {
+    if (ptr) GeoIP_delete(ptr);
+  };
+};
+
+class GeoIPInterfaceDAT : public GeoIPInterface {
+public:
+  GeoIPInterfaceDAT(const string &fname, const string &modeStr) {
+    int flags;
+    if (modeStr == "standard")
+      flags = GEOIP_STANDARD;
+    else if (modeStr == "memory")
+      flags = GEOIP_MEMORY_CACHE;
+    else if (modeStr == "index")
+      flags = GEOIP_INDEX_CACHE;
+    #ifdef HAVE_MMAP
+    else if (modeStr == "mmap")
+      flags = GEOIP_MMAP_CACHE;
+    #endif
+    else
+      throw PDNSException("Invalid cache mode " + modeStr + " for GeoIP backend");
+
+    d_gi = std::unique_ptr<GeoIP,geoip_deleter>(GeoIP_open(fname.c_str(), flags));
+    if (d_gi.get() == NULL)
+      throw PDNSException("Cannot open GeoIP database " + fname);
+    d_db_type = GeoIP_database_edition(d_gi.get());
+  }
+
+  bool queryCountry(string &ret, GeoIPLookup* gl, const string &ip) override {
+    if (d_db_type == GEOIP_COUNTRY_EDITION ||
+        d_db_type == GEOIP_LARGE_COUNTRY_EDITION) {
+      ret = GeoIP_code3_by_id(GeoIP_id_by_addr_gl(d_gi.get(), ip.c_str(), gl));
+      return true;
+    } else if (d_db_type == GEOIP_REGION_EDITION_REV0 ||
+               d_db_type == GEOIP_REGION_EDITION_REV1) {
+      GeoIPRegion* gir = GeoIP_region_by_addr_gl(d_gi.get(), ip.c_str(), gl);
+      if (gir) {
+        ret = GeoIP_code3_by_id(GeoIP_id_by_code(gir->country_code));
+        return true;
+      }
+    } else if (d_db_type == GEOIP_CITY_EDITION_REV0 ||
+               d_db_type == GEOIP_CITY_EDITION_REV1) {
+      GeoIPRecord *gir = GeoIP_record_by_addr(d_gi.get(), ip.c_str());
+      if (gir) {
+        ret = gir->country_code3;
+        gl->netmask = gir->netmask;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool queryCountryV6(string &ret, GeoIPLookup* gl, const string &ip) override {
+    if (d_db_type == GEOIP_COUNTRY_EDITION_V6 ||
+        d_db_type == GEOIP_LARGE_COUNTRY_EDITION_V6) {
+      ret = GeoIP_code3_by_id(GeoIP_id_by_addr_v6_gl(d_gi.get(), ip.c_str(), gl));
+      return true;
+    } else if (d_db_type == GEOIP_REGION_EDITION_REV0 ||
+               d_db_type == GEOIP_REGION_EDITION_REV1) {
+      GeoIPRegion* gir = GeoIP_region_by_addr_v6_gl(d_gi.get(), ip.c_str(), gl);
+      if (gir) {
+        ret = GeoIP_code3_by_id(GeoIP_id_by_code(gir->country_code));
+        return true;
+      }
+    } else if (d_db_type == GEOIP_CITY_EDITION_REV0_V6 ||
+               d_db_type == GEOIP_CITY_EDITION_REV1_V6) {
+      GeoIPRecord *gir = GeoIP_record_by_addr_v6(d_gi.get(), ip.c_str());
+      if (gir) {
+        ret = gir->country_code3;
+        gl->netmask = gir->netmask;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool queryCountry2(string &ret, GeoIPLookup* gl, const string &ip) override {
+    if (d_db_type == GEOIP_COUNTRY_EDITION ||
+        d_db_type == GEOIP_LARGE_COUNTRY_EDITION) {
+      ret = GeoIP_code_by_id(GeoIP_id_by_addr_gl(d_gi.get(), ip.c_str(), gl));
+      return true;
+    } else if (d_db_type == GEOIP_REGION_EDITION_REV0 ||
+               d_db_type == GEOIP_REGION_EDITION_REV1) {
+      GeoIPRegion* gir = GeoIP_region_by_addr_gl(d_gi.get(), ip.c_str(), gl);
+      if (gir) {
+        ret = GeoIP_code_by_id(GeoIP_id_by_code(gir->country_code));
+        return true;
+      }
+    } else if (d_db_type == GEOIP_CITY_EDITION_REV0 ||
+               d_db_type == GEOIP_CITY_EDITION_REV1) {
+      GeoIPRecord *gir = GeoIP_record_by_addr(d_gi.get(), ip.c_str());
+      if (gir) {
+        ret = gir->country_code;
+        gl->netmask = gir->netmask;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool queryCountry2V6(string &ret, GeoIPLookup* gl, const string &ip) override {
+    if (d_db_type == GEOIP_COUNTRY_EDITION_V6 ||
+        d_db_type == GEOIP_LARGE_COUNTRY_EDITION_V6) {
+      ret = GeoIP_code_by_id(GeoIP_id_by_addr_v6_gl(d_gi.get(), ip.c_str(), gl));
+      return true;
+    } else if (d_db_type == GEOIP_REGION_EDITION_REV0 ||
+               d_db_type == GEOIP_REGION_EDITION_REV1) {
+      GeoIPRegion* gir = GeoIP_region_by_addr_v6_gl(d_gi.get(), ip.c_str(), gl);
+      if (gir) {
+        ret = GeoIP_code_by_id(GeoIP_id_by_code(gir->country_code));
+        return true;
+      }
+    } else if (d_db_type == GEOIP_CITY_EDITION_REV0_V6 ||
+               d_db_type == GEOIP_CITY_EDITION_REV1_V6) {
+      GeoIPRecord *gir = GeoIP_record_by_addr_v6(d_gi.get(), ip.c_str());
+      if (gir) {
+        ret = gir->country_code;
+        gl->netmask = gir->netmask;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool queryContinent(string &ret, GeoIPLookup* gl, const string &ip) override {
+    if (d_db_type == GEOIP_COUNTRY_EDITION ||
+        d_db_type == GEOIP_LARGE_COUNTRY_EDITION) {
+      ret = GeoIP_continent_by_id(GeoIP_id_by_addr_gl(d_gi.get(), ip.c_str(), gl));
+      return true;
+    } else if (d_db_type == GEOIP_REGION_EDITION_REV0 ||
+               d_db_type == GEOIP_REGION_EDITION_REV1) {
+      GeoIPRegion* gir = GeoIP_region_by_addr_gl(d_gi.get(), ip.c_str(), gl);
+      if (gir) {
+        ret = GeoIP_continent_by_id(GeoIP_id_by_code(gir->country_code));
+        return true;
+      }
+    } else if (d_db_type == GEOIP_CITY_EDITION_REV0 ||
+               d_db_type == GEOIP_CITY_EDITION_REV1) {
+      GeoIPRecord *gir = GeoIP_record_by_addr(d_gi.get(), ip.c_str());
+      if (gir) {
+        ret =  ret = GeoIP_continent_by_id(GeoIP_id_by_code(gir->country_code));
+        gl->netmask = gir->netmask;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool queryContinentV6(string &ret, GeoIPLookup* gl, const string &ip) override {
+    if (d_db_type == GEOIP_COUNTRY_EDITION_V6 ||
+        d_db_type == GEOIP_LARGE_COUNTRY_EDITION_V6) {
+      ret = GeoIP_continent_by_id(GeoIP_id_by_addr_v6_gl(d_gi.get(), ip.c_str(), gl));
+      return true;
+    } else if (d_db_type == GEOIP_REGION_EDITION_REV0 ||
+               d_db_type == GEOIP_REGION_EDITION_REV1) {
+      GeoIPRegion* gir = GeoIP_region_by_addr_v6_gl(d_gi.get(), ip.c_str(), gl);
+      if (gir) {
+        ret = GeoIP_continent_by_id(GeoIP_id_by_code(gir->country_code));
+        return true;
+      }
+    } else if (d_db_type == GEOIP_CITY_EDITION_REV0_V6 ||
+               d_db_type == GEOIP_CITY_EDITION_REV1_V6) {
+      GeoIPRecord *gir = GeoIP_record_by_addr_v6(d_gi.get(), ip.c_str());
+      if (gir) {
+        ret = GeoIP_continent_by_id(GeoIP_id_by_code(gir->country_code));
+        gl->netmask = gir->netmask;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool queryName(string &ret, GeoIPLookup* gl, const string &ip) override {
+    if (d_db_type == GEOIP_ISP_EDITION ||
+        d_db_type == GEOIP_ORG_EDITION) {
+      string val = valueOrEmpty<char*,string>(GeoIP_name_by_addr_gl(d_gi.get(), ip.c_str(), gl));
+      if (!val.empty()) {
+        // reduce space to dash
+        ret = boost::replace_all_copy(val, " ", "-");
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool queryNameV6(string &ret, GeoIPLookup* gl, const string &ip) override {
+    if (d_db_type == GEOIP_ISP_EDITION_V6 ||
+        d_db_type == GEOIP_ORG_EDITION_V6) {
+      string val = valueOrEmpty<char*,string>(GeoIP_name_by_addr_v6_gl(d_gi.get(), ip.c_str(), gl));
+      if (!val.empty()) {
+        // reduce space to dash
+        ret = boost::replace_all_copy(val, " ", "-");
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool queryASnum(string &ret, GeoIPLookup* gl, const string &ip) override {
+    if (d_db_type == GEOIP_ASNUM_EDITION) {
+      string val = valueOrEmpty<char*,string>(GeoIP_name_by_addr_gl(d_gi.get(), ip.c_str(), gl));
+      if (!val.empty()) {
+        vector<string> asnr;
+        stringtok(asnr, val);
+        if(asnr.size()>0) {
+          ret = asnr[0];
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  bool queryASnumV6(string &ret, GeoIPLookup* gl, const string &ip) override {
+    if (d_db_type == GEOIP_ASNUM_EDITION_V6) {
+      string val = valueOrEmpty<char*,string>(GeoIP_name_by_addr_v6_gl(d_gi.get(), ip.c_str(), gl));
+      if (!val.empty()) {
+        vector<string> asnr;
+        stringtok(asnr, val);
+        if(asnr.size()>0) {
+          ret = asnr[0];
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  bool queryRegion(string &ret, GeoIPLookup* gl, const string &ip) override {
+    if (d_db_type == GEOIP_REGION_EDITION_REV0 ||
+        d_db_type == GEOIP_REGION_EDITION_REV1) {
+      GeoIPRegion *gir = GeoIP_region_by_addr_gl(d_gi.get(), ip.c_str(), gl);
+      if (gir) {
+        ret = valueOrEmpty<char*,string>(gir->region);
+        return true;
+      }
+    } else if (d_db_type == GEOIP_CITY_EDITION_REV0 ||
+               d_db_type == GEOIP_CITY_EDITION_REV1) {
+      GeoIPRecord *gir = GeoIP_record_by_addr(d_gi.get(), ip.c_str());
+      if (gir) {
+        ret = valueOrEmpty<char*,string>(gir->region);
+        gl->netmask = gir->netmask;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool queryRegionV6(string &ret, GeoIPLookup* gl, const string &ip) override {
+    if (d_db_type == GEOIP_REGION_EDITION_REV0 ||
+        d_db_type == GEOIP_REGION_EDITION_REV1) {
+      GeoIPRegion *gir = GeoIP_region_by_addr_v6_gl(d_gi.get(), ip.c_str(), gl);
+      if (gir) {
+        ret = valueOrEmpty<char*,string>(gir->region);
+        return true;
+      }
+    } else if (d_db_type == GEOIP_CITY_EDITION_REV0_V6 ||
+               d_db_type == GEOIP_CITY_EDITION_REV1_V6) {
+      GeoIPRecord *gir = GeoIP_record_by_addr_v6(d_gi.get(), ip.c_str());
+      if (gir) {
+        ret = valueOrEmpty<char*,string>(gir->region);
+        gl->netmask = gir->netmask;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool queryCity(string &ret, GeoIPLookup* gl, const string &ip) override {
+    if (d_db_type == GEOIP_CITY_EDITION_REV0 ||
+        d_db_type == GEOIP_CITY_EDITION_REV1) {
+      GeoIPRecord *gir = GeoIP_record_by_addr(d_gi.get(), ip.c_str());
+      if (gir) {
+        ret = valueOrEmpty<char*,string>(gir->city);
+        gl->netmask = gir->netmask;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool queryCityV6(string &ret, GeoIPLookup* gl, const string &ip) override {
+    if (d_db_type == GEOIP_CITY_EDITION_REV0_V6 ||
+        d_db_type == GEOIP_CITY_EDITION_REV1_V6) {
+      GeoIPRecord *gir = GeoIP_record_by_addr_v6(d_gi.get(), ip.c_str());
+      if (gir) {
+        ret = valueOrEmpty<char*,string>(gir->city);
+        gl->netmask = gir->netmask;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  ~GeoIPInterfaceDAT() { }
+private:
+  unsigned int d_db_type;
+  unique_ptr<GeoIP,geoip_deleter> d_gi;
+};
+
+unique_ptr<GeoIPInterface> GeoIPInterface::makeDATInterface(const string &fname, const map<string, string>& opts) {
+  string mode = "standard";
+  const auto &opt = opts.find("mode");
+  if (opt != opts.end())
+    mode = opt->second;
+  return unique_ptr<GeoIPInterface>(new GeoIPInterfaceDAT(fname, mode));
+}
diff --git a/modules/geoipbackend/geoipinterface.cc b/modules/geoipbackend/geoipinterface.cc
new file mode 100644 (file)
index 0000000..31c6eac
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "geoipbackend.hh"
+#include "geoipinterface.hh"
+
+unique_ptr<GeoIPInterface> GeoIPInterface::makeInterface(const string& dbStr) {
+  /* parse dbStr */
+  map<string, string> opts;
+  vector<string> parts1, parts2;
+  string driver;
+  string filename;
+  stringtok(parts1, dbStr, ":");
+
+  if (parts1.size() == 1) {
+    stringtok(parts2, parts1[0], ";");
+    /* try extension */
+    filename = parts2[0];
+    size_t pos = filename.find_last_of(".");
+    if (pos != string::npos)
+      driver = driver.substr(pos+1);
+    else
+      driver = "unknown";
+  } else {
+    driver = parts1[0];
+    stringtok(parts2, parts1[1], ";");
+    filename = parts2[0];
+  }
+
+  if (parts2.size() > 1) {
+     parts2.erase(parts2.begin(), parts2.begin()+1);
+     for(const auto &opt: parts2) {
+       vector<string> kv;
+       stringtok(kv, opt, "=");
+       opts[kv[0]] = kv[1];
+     }
+  }
+
+  if (driver == "dat") {
+     return makeDATInterface(filename, opts);
+  } else {
+     throw PDNSException(string("Unsupported file type '") + driver + string("' (use type: prefix to force type)"));
+  }
+}
diff --git a/modules/geoipbackend/geoipinterface.hh b/modules/geoipbackend/geoipinterface.hh
new file mode 100644 (file)
index 0000000..c307ad6
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef PDNS_GEOIPINTERFACE_HH
+#define PDNS_GEOIPINTERFACE_HH
+
+class GeoIPInterface {
+public:
+  enum GeoIPQueryAttribute {
+    ASn,
+    City,
+    Continent,
+    Country,
+    Country2,
+    Name,
+    Region
+  };
+
+  virtual bool queryCountry(string &ret, GeoIPLookup* gl, const string &ip) = 0;
+  virtual bool queryCountryV6(string &ret, GeoIPLookup* gl, const string &ip) = 0;
+  virtual bool queryCountry2(string &ret, GeoIPLookup* gl, const string &ip) = 0;
+  virtual bool queryCountry2V6(string &ret, GeoIPLookup* gl, const string &ip) = 0;
+  virtual bool queryContinent(string &ret, GeoIPLookup* gl, const string &ip) = 0;
+  virtual bool queryContinentV6(string &ret, GeoIPLookup* gl, const string &ip) = 0;
+  virtual bool queryName(string &ret, GeoIPLookup* gl, const string &ip) = 0;
+  virtual bool queryNameV6(string &ret, GeoIPLookup* gl, const string &ip) = 0;
+  virtual bool queryASnum(string &ret, GeoIPLookup* gl, const string &ip) = 0;
+  virtual bool queryASnumV6(string &ret, GeoIPLookup* gl, const string &ip) = 0;
+  virtual bool queryRegion(string &ret, GeoIPLookup* gl, const string &ip) = 0;
+  virtual bool queryRegionV6(string &ret, GeoIPLookup* gl, const string &ip) = 0;
+  virtual bool queryCity(string &ret, GeoIPLookup* gl, const string &ip) = 0;
+  virtual bool queryCityV6(string &ret, GeoIPLookup* gl, const string &ip) = 0;
+
+  virtual ~GeoIPInterface() { }
+
+  static unique_ptr<GeoIPInterface> makeInterface(const string& dbStr);
+private:
+  static unique_ptr<GeoIPInterface> makeDATInterface(const string& fname, const map<string, string>& opts);
+};
+
+#endif