]> granicus.if.org Git - pdns/commitdiff
wip
authorbert hubert <bert.hubert@netherlabs.nl>
Fri, 8 Dec 2017 19:45:17 +0000 (20:45 +0100)
committerbert hubert <bert.hubert@netherlabs.nl>
Fri, 8 Dec 2017 19:45:17 +0000 (20:45 +0100)
modules/geoipbackend/geoipbackend.cc
modules/geoipbackend/geoipbackend.hh
pdns/Makefile.am
pdns/lua-auth4.cc
pdns/lua-record.cc [new file with mode: 0644]
pdns/packethandler.cc

index b02b475d34a9792a721b8ffc345fd027dd7fc80e..7bbdd4236428cdfe92356ee413bf890bd64df40a 100644 (file)
@@ -47,7 +47,7 @@ public:
 };
 
 static vector<GeoIPDomain> s_domains;
-static int s_rc = 0; // refcount
+static int s_rc = 0; // refcount - always accessed under lock
 
 struct geoip_deleter {
   void operator()(GeoIP* ptr) {
@@ -125,7 +125,8 @@ void GeoIPBackend::initialize() {
   if (s_geoip_files.empty())
     L<<Logger::Warning<<"No GeoIP database files loaded!"<<endl;
 
-  config = YAML::LoadFile(getArg("zones-file"));
+  if(!getArg("zones-file").empty())
+    config = YAML::LoadFile(getArg("zones-file"));
 
   for(YAML::Node domain :  config["domains"]) {
     GeoIPDomain dom;
@@ -444,6 +445,37 @@ bool GeoIPBackend::queryCountry(string &ret, GeoIPLookup* gl, const string &ip,
   return false;
 }
 
+bool GeoIPBackend::queryLatLon(double& lat, double& lon, 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) {
+      lat = gir->latitude;
+      lon = gir->longitude;
+      gl->netmask = gir->netmask;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool GeoIPBackend::queryLatLonV6(double& lat, double& lon, 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) {
+      lat = gir->latitude;
+      lon = gir->longitude;
+      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) {
@@ -690,6 +722,7 @@ bool GeoIPBackend::queryCityV6(string &ret, GeoIPLookup* gl, const string &ip, c
 string GeoIPBackend::queryGeoIP(const string &ip, bool v6, GeoIPQueryAttribute attribute, GeoIPLookup* gl) {
   string ret = "unknown";
 
+  double lat=0, lon=0;
   for(auto const& gi: s_geoip_files) {
     string val;
     bool found = false;
@@ -723,6 +756,12 @@ string GeoIPBackend::queryGeoIP(const string &ip, bool v6, GeoIPQueryAttribute a
       if (v6) found = queryCityV6(val, gl, ip, gi);
       else found = queryCity(val, gl, ip, gi);
       break;
+    case LatLon:
+      if (v6) found = queryLatLonV6(lat, lon, gl, ip, gi);
+      else found = queryLatLon(lat, lon, gl, ip, gi);
+      val = std::to_string(lat)+" "+std::to_string(lon);
+      break;
+
     }
 
     if (!found || val.empty() || val == "--") continue; // try next database
index 6f0c67898053bb793e843d43f163277d7a318b67..02a66a4e80f7af1f3cee3aa26d7cdec005fca17e 100644 (file)
@@ -75,7 +75,8 @@ public:
     Country,
     Country2,
     Name,
-    Region
+    Region,
+    LatLon
   };
 
 private:
@@ -83,7 +84,11 @@ private:
 
   void initialize();
   void ip2geo(const GeoIPDomain& dom, const string& qname, const string& ip);
+public:
   string queryGeoIP(const string &ip, bool v6, GeoIPQueryAttribute attribute, GeoIPLookup* gl);
+  bool queryLatLon(double& lat, double& lon, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
+  bool queryLatLonV6(double& lat, double& lon, GeoIPLookup* gl, const string &ip, const geoip_file_t& gi);
+private:
   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);
@@ -98,6 +103,8 @@ private:
   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 hasDNSSECkey(const DNSName& name);
index a37181f3a5ee0a06634d2b2fc9b1eafe00e30503..040ad51b26a6004438bee79ebd9139d49c2bda57 100644 (file)
@@ -182,6 +182,7 @@ pdns_server_SOURCES = \
        lua-auth.cc lua-auth.hh \
        lua-auth4.cc lua-auth4.hh \
        lua-pdns.cc lua-pdns.hh lua-iputils.cc \
+       lua-record.cc \
        minicurl.cc minicurl.hh \
        mastercommunicator.cc \
        md5.hh \
index 61d7e1a16cc41a1137f3f918eb5afe57370a71b4..30fd56d96a6c6ecab755b68e5b58d6e41ece4e0c 100644 (file)
@@ -1,3 +1,7 @@
+#include "config.h"
+#if defined(HAVE_LUA)
+#include "ext/luawrapper/include/LuaContext.hpp"
+#endif
 #include "lua-auth4.hh"
 #include "stubresolver.hh"
 #include <fstream>
@@ -9,8 +13,9 @@
 #include "sstuff.hh"
 #include <thread>
 #include <mutex>
-#include "minicurl.hh"
+
 #include "ueberbackend.hh"
+
 #if !defined(HAVE_LUA)
 
 AuthLua4::AuthLua4(const std::string& fname) { }
@@ -21,9 +26,6 @@ AuthLua4::~AuthLua4() { }
 
 #else
 
-#undef L
-#include "ext/luawrapper/include/LuaContext.hpp"
-
 LuaContext* AuthLua4::getLua()
 {
   return d_lw.get();
@@ -295,356 +297,4 @@ bool AuthLua4::updatePolicy(const DNSName &qname, QType qtype, const DNSName &zo
 AuthLua4::~AuthLua4() { }
 
 
-class IsUpOracle
-{
-private:
-  typedef std::unordered_map<string,string> opts_t;
-  struct CheckDesc
-  {
-    ComboAddress rem;
-    string url;
-    opts_t opts;
-    bool operator<(const CheckDesc& rhs) const
-    {
-      return std::make_tuple(rem, url) <
-        std::make_tuple(rhs.rem, rhs.url);
-    }
-  };
-public:
-  bool isUp(const ComboAddress& remote);
-  bool isUp(const ComboAddress& remote, const std::string& url, opts_t opts=opts_t());
-    
-private:
-  void checkURLThread(ComboAddress rem, std::string url, opts_t opts);
-  void checkTCPThread(const ComboAddress& rem) {
-    CheckDesc cd{rem};
-    setDown(cd);
-    for(bool first=true;;first=false) {
-      try {
-        Socket s(rem.sin4.sin_family, SOCK_STREAM);
-        s.setNonBlocking();
-        s.connect(rem, 1);
-        if(!d_statuses[{rem,string()}].second)
-          cout<<"Declaring "<<rem.toStringWithPort()<<" UP!"<<endl;
-        setUp(cd);
-      }
-      catch(NetworkError& ne) {
-        if(d_statuses[{rem,string()}].second || first)
-          cout<<"Failed to connect to "<<rem.toStringWithPort()<<", setting DOWN"<<endl;
-        setDown(cd);
-      }
-      sleep(1);
-    }
-  }
-
-
-  map<CheckDesc, pair<std::thread*, bool>> d_statuses;
-
-  std::mutex d_mutex;
-
-  void setStatus(const CheckDesc& cd, bool status) 
-  {
-    std::lock_guard<std::mutex> l(d_mutex);
-    d_statuses[cd].second=status;
-  }
-
-  void setDown(const ComboAddress& rem, const std::string& url=std::string(), opts_t opts=opts_t())
-  {
-    CheckDesc cd{rem, url, opts};
-    setStatus(cd, false);
-  }
-
-  void setUp(const ComboAddress& rem, const std::string& url=std::string(), opts_t opts=opts_t())
-  {
-    CheckDesc cd{rem, url, opts};
-    setStatus(cd, true);
-  }
-
-  void setDown(const CheckDesc& cd)
-  {
-    setStatus(cd, false);
-  }
-
-  void setUp(const CheckDesc& cd)
-  {
-    setStatus(cd, true);
-  }
-
-  bool upStatus(const ComboAddress& rem, const std::string& url=std::string(), opts_t opts=opts_t())
-  {
-    CheckDesc cd{rem, url, opts};
-    std::lock_guard<std::mutex> l(d_mutex);
-    return d_statuses[cd].second;
-  }
-
-};
-
-bool IsUpOracle::isUp(const ComboAddress& remote)
-{
-  std::lock_guard<std::mutex> l(d_mutex);
-  CheckDesc cd{remote};
-  auto iter = d_statuses.find(cd);
-  if(iter == d_statuses.end()) {
-    cout<<"First ever query for "<<remote.toStringWithPort()<<", launching checker"<<endl;
-    std::thread* checker = new std::thread(&IsUpOracle::checkTCPThread, this, remote);
-    d_statuses[cd]={checker, false};
-    return false;
-  }
-  return iter->second.second;
-}
-
-bool IsUpOracle::isUp(const ComboAddress& remote, const std::string& url, std::unordered_map<string,string> opts)
-{
-  CheckDesc cd{remote, url, opts};
-  std::lock_guard<std::mutex> l(d_mutex);
-  auto iter = d_statuses.find(cd);
-  if(iter == d_statuses.end()) {
-    //    cout<<"First ever query for "<<remote.toString()<<" and url "<<url<<", launching checker"<<endl;
-    std::thread* checker = new std::thread(&IsUpOracle::checkURLThread, this, remote, url, opts);
-    d_statuses[cd]={checker, false};
-    return false;
-  }
-  return iter->second.second;
-}
-
-void IsUpOracle::checkURLThread(ComboAddress rem, std::string url, opts_t opts) 
-{
-  setDown(rem, url, opts);
-  for(bool first=true;;first=false) {
-    try {
-      MiniCurl mc;
-      //      cout<<"Checking URL "<<url<<" at "<<rem.toString()<<endl;
-      string content=mc.getURL(url, &rem);
-      if(opts.count("stringmatch") && content.find(opts["stringmatch"]) == string::npos) {
-        //        cout<<"URL "<<url<<" is up at "<<rem.toString()<<", but could not find stringmatch "<<opts["stringmatch"]<<" in page content, setting DOWN"<<endl;
-        setDown(rem, url, opts);
-        goto loop;
-      }
-      if(!upStatus(rem,url))
-        ; // cout<<"Declaring "<<rem.toString()<<" UP for URL "<<url<<"!"<<endl;
-      setUp(rem, url);
-    }
-    catch(std::exception& ne) {
-      if(upStatus(rem,url,opts) || first)
-        ; // cout<<"Failed to connect to "<<rem.toString()<<" for URL "<<url<<", setting DOWN, error: "<<ne.what()<<endl;
-      setDown(rem,url);
-    }
-  loop:;
-    sleep(5);
-  }
-}
-
-
-IsUpOracle g_up;
-
-std::vector<shared_ptr<DNSRecordContent>> luaSynth(const std::string& code, const DNSName& query, const DNSName& zone, int zoneid, const DNSPacket& dnsp, uint16_t qtype) 
-{
-  AuthLua4 alua("");
-  std::vector<shared_ptr<DNSRecordContent>> ret;
-  
-  LuaContext& lua = *alua.getLua();
-  lua.writeVariable("qname", query);
-  lua.writeVariable("who", dnsp.getRemote());
-  ComboAddress bestwho;
-  if(dnsp.hasEDNSSubnet()) {
-    lua.writeVariable("ecs-who", dnsp.getRealRemote());
-    bestwho=dnsp.getRealRemote().getNetwork();
-    lua.writeVariable("best-who", dnsp.getRealRemote().getNetwork());
-  }
-  else {
-    lua.writeVariable("ecs-who", dnsp.getRemote());
-    bestwho=dnsp.getRemote();
-  }
-
-  
-  
-  lua.writeFunction("ifportup", [](int port, const vector<pair<int, string> >& ips) {
-      vector<ComboAddress> candidates;
-      for(const auto& i : ips) {
-        ComboAddress rem(i.second, port);
-        if(g_up.isUp(rem))
-          candidates.push_back(rem);
-      }
-      //      cout<<"Have "<<candidates.size()<<" candidate IP addresses: ";
-      for(const auto& c : candidates)
-        cout<<c.toString()<<" ";
-      cout<<endl;
-      vector<string> ret;
-      if(candidates.empty()) {
-        //        cout<<"Everything is down. Returning all of them"<<endl;
-        for(const auto& i : ips) 
-          ret.push_back(i.second);
-      }
-      else
-        ret.push_back(candidates[random() % candidates.size()].toString());
-      return ret;
-    });
-
-
-  lua.writeFunction("ifurlup", [](const std::string& url,
-                                  const boost::variant<
-                                  vector<pair<int, string> >,
-                                  vector<pair<int, vector<pair<int, string> > > >
-                                  > & ips, boost::optional<std::unordered_map<string,string>> options) {
-
-      vector<vector<ComboAddress> > candidates;
-      std::unordered_map<string,string> opts;
-      if(options)
-        opts = *options;
-      if(auto simple = boost::get<vector<pair<int,string>>>(&ips)) {
-        vector<ComboAddress> unit;
-        for(const auto& i : *simple) {
-          ComboAddress rem(i.second, 80);
-          unit.push_back(rem);
-        }
-        candidates.push_back(unit);
-      } else {
-        auto units = boost::get<vector<pair<int, vector<pair<int, string> > > >>(ips);
-        for(const auto& u : units) {
-          vector<ComboAddress> unit;
-          for(const auto& c : u.second) {
-            ComboAddress rem(c.second, 80);
-            unit.push_back(rem);
-          }
-          candidates.push_back(unit);
-        }
-      }
-
-      //
-      //      cout<<"Have "<<candidates.size()<<" units of IP addresses: "<<endl;
-      int ucount=1;
-      for(const auto& unit : candidates) {
-        //        cout<<"Unit "<<ucount<<": ";
-        for(const auto& c : unit)
-          cout<<c.toString()<<" ";
-        cout<<endl;
-        ucount++;
-      }
-      vector<string> ret;
-      for(const auto& unit : candidates) {
-        vector<ComboAddress> available;
-        for(const auto& c : unit)
-          if(g_up.isUp(c, url, opts))
-            available.push_back(c);
-        if(available.empty()) {
-          //  cerr<<"Entire unit is down, trying next one if available"<<endl;
-          continue;
-        }
-        ret.push_back(available[random() % available.size()].toString());
-        return ret;
-      }      
-      //      cerr<<"ALL units are down, returning all IP addresses"<<endl;
-      for(const auto& unit : candidates) {
-        for(const auto& c : unit)
-          ret.push_back(c.toString());
-      }
-
-      return ret;
-                    });
-
-  
-  lua.writeFunction("pickrandom", [](const vector<pair<int, string> >& ips) {
-      return ips[random()%ips.size()].second;
-    });
-
-  // wrandom({ {100, '1.2.3.4'}, {50, '5.4.3.2'}, {1, '192.168.1.0'}})"
-
-  lua.writeFunction("wrandom", [](std::unordered_map<int, std::unordered_map<int, string> > ips) {
-      int sum=0;
-      vector<pair<int, string> > pick;
-      for(auto& i : ips) {
-        sum += atoi(i.second[1].c_str());
-        pick.push_back({sum, i.second[2]});
-      }
-      int r = random() % sum;
-      auto p = upper_bound(pick.begin(), pick.end(),r, [](int r, const decltype(pick)::value_type& a) { return  r < a.first;});
-      return p->second;
-      
-    });
-
-  int counter=0;
-  lua.writeFunction("report", [&counter](string event, boost::optional<string> line){
-      throw std::runtime_error("Script took too long");
-    });
-  lua.executeCode("debug.sethook(report, '', 1000)");
-
-  lua.writeFunction("continent", [](const std::string& continent) {
-      return true;
-    });
-
-  lua.writeFunction("country", [](const std::string& country) {
-      return true;
-    });
-
-  lua.writeFunction("netmask", [bestwho](const vector<pair<int,string>>& ips) {
-      for(const auto& i :ips) {
-        Netmask nm(i.second);
-        if(nm.match(bestwho))
-          return true;
-      }
-      return false;
-    });
-
-  /* {
-       {
-        {'192.168.0.0/16', '10.0.0.0/8'}, 
-        {'192.168.20.20', '192.168.20.21'}
-       },
-       {
-        {'0.0.0.0/0'}, {'192.0.2.1'}
-       }
-     }
-  */  
-  lua.writeFunction("view", [bestwho](const vector<pair<int, vector<pair<int, vector<pair<int, string> > > > > >& in) {
-      for(const auto& rule : in) {
-        const auto& netmasks=rule.second[0].second;
-        const auto& destinations=rule.second[1].second;
-        for(const auto& nmpair : netmasks) {
-          Netmask nm(nmpair.second);
-          if(nm.match(bestwho)) {
-            return destinations[random() % destinations.size()].second;
-          }
-        }
-      }
-      return std::string();
-    }
-    );
-  
-  
-  lua.writeFunction("include", [&lua,zone,zoneid](string record) {
-      try {
-        UeberBackend ub;
-        ub.lookup(QType(QType::LUA), DNSName(record) +zone, 0, zoneid);
-        DNSZoneRecord dr;
-        while(ub.get(dr)) {
-          auto lr = getRR<LUARecordContent>(dr.dr);
-          lua.executeCode(lr->getCode());
-        }
-      }catch(std::exception& e) { cerr<<"Oops: "<<e.what()<<endl; }
-    });
-
-  
-  try {
-    auto content=lua.executeCode<boost::variant<string, vector<pair<int, string> > > >(code);
-    //  cout<<"Counter: "<<counter<<endl;
-    vector<string> contents;
-    if(auto str = boost::get<string>(&content))
-      contents.push_back(*str);
-    else
-      for(const auto& c : boost::get<vector<pair<int,string>>>(content))
-        contents.push_back(c.second);
-    
-    for(const auto& content: contents) {
-      if(qtype==QType::TXT)
-        ret.push_back(std::shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(qtype, 1, '"'+content+'"' )));
-      else
-        ret.push_back(std::shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(qtype, 1, content )));
-    }
-  }catch(std::exception &e) {
-    cerr<<"Lua reported: "<<e.what()<<endl;
-  }
-
-  return ret;
-}
-
 #endif
diff --git a/pdns/lua-record.cc b/pdns/lua-record.cc
new file mode 100644 (file)
index 0000000..9c16682
--- /dev/null
@@ -0,0 +1,492 @@
+#include "ext/luawrapper/include/LuaContext.hpp"
+#include "lua-auth4.hh"
+#include <thread>
+#include "sstuff.hh"
+#include <mutex>
+#include "minicurl.hh"
+#include "ueberbackend.hh"
+#include "../../modules/geoipbackend/geoipbackend.hh"
+class IsUpOracle
+{
+private:
+  typedef std::unordered_map<string,string> opts_t;
+  struct CheckDesc
+  {
+    ComboAddress rem;
+    string url;
+    opts_t opts;
+    bool operator<(const CheckDesc& rhs) const
+    {
+      return std::make_tuple(rem, url) <
+        std::make_tuple(rhs.rem, rhs.url);
+    }
+  };
+public:
+  bool isUp(const ComboAddress& remote);
+  bool isUp(const ComboAddress& remote, const std::string& url, opts_t opts=opts_t());
+    
+private:
+  void checkURLThread(ComboAddress rem, std::string url, opts_t opts);
+  void checkTCPThread(const ComboAddress& rem) {
+    CheckDesc cd{rem};
+    setDown(cd);
+    for(bool first=true;;first=false) {
+      try {
+        Socket s(rem.sin4.sin_family, SOCK_STREAM);
+        s.setNonBlocking();
+        s.connect(rem, 1);
+        if(!isUp(rem))
+          L<<Logger::Warning<<"Lua record monitoring declaring TCP/IP "<<rem.toStringWithPort()<<" UP!"<<endl;
+        setUp(cd);
+      }
+      catch(NetworkError& ne) {
+        if(isUp(rem) || first)
+          L<<Logger::Warning<<"Lua record monitoring declaring TCP/IP "<<rem.toStringWithPort()<<" DOWN!"<<endl;
+        setDown(cd);
+      }
+      sleep(1);
+    }
+  }
+
+
+  struct Checker
+  {
+    std::thread* thr;
+    bool status;
+  };
+
+  typedef map<CheckDesc, Checker> statuses_t;
+  statuses_t d_statuses;
+  
+  std::mutex d_mutex;
+
+  void setStatus(const CheckDesc& cd, bool status) 
+  {
+    std::lock_guard<std::mutex> l(d_mutex);
+    d_statuses[cd].status=status;
+  }
+
+  void setDown(const ComboAddress& rem, const std::string& url=std::string(), opts_t opts=opts_t())
+  {
+    CheckDesc cd{rem, url, opts};
+    setStatus(cd, false);
+  }
+
+  void setUp(const ComboAddress& rem, const std::string& url=std::string(), opts_t opts=opts_t())
+  {
+    CheckDesc cd{rem, url, opts};
+    setStatus(cd, true);
+  }
+
+  void setDown(const CheckDesc& cd)
+  {
+    setStatus(cd, false);
+  }
+
+  void setUp(const CheckDesc& cd)
+  {
+    setStatus(cd, true);
+  }
+
+  bool upStatus(const ComboAddress& rem, const std::string& url=std::string(), opts_t opts=opts_t())
+  {
+    CheckDesc cd{rem, url, opts};
+    std::lock_guard<std::mutex> l(d_mutex);
+    return d_statuses[cd].status;
+  }
+
+  statuses_t getStatus()
+  {
+    std::lock_guard<std::mutex> l(d_mutex);
+    return d_statuses;
+  }
+
+};
+
+bool IsUpOracle::isUp(const ComboAddress& remote)
+{
+  std::lock_guard<std::mutex> l(d_mutex);
+  CheckDesc cd{remote};
+  auto iter = d_statuses.find(cd);
+  if(iter == d_statuses.end()) {
+    L<<Logger::Warning<<"Launching TCP/IP status checker for "<<remote.toStringWithPort()<<endl;
+    std::thread* checker = new std::thread(&IsUpOracle::checkTCPThread, this, remote);
+    d_statuses[cd]=Checker{checker, false};
+    return false;
+  }
+  return iter->second.status;
+}
+
+bool IsUpOracle::isUp(const ComboAddress& remote, const std::string& url, std::unordered_map<string,string> opts)
+{
+  CheckDesc cd{remote, url, opts};
+  std::lock_guard<std::mutex> l(d_mutex);
+  auto iter = d_statuses.find(cd);
+  if(iter == d_statuses.end()) {
+    //    L<<Logger::Warning<<"Launching HTTP(s) status checker for "<<remote.toStringWithPort()<<" and URL "<<url<<endl;
+    std::thread* checker = new std::thread(&IsUpOracle::checkURLThread, this, remote, url, opts);
+    d_statuses[cd]=Checker{checker, false};
+    return false;
+  }
+  
+  return iter->second.status;
+}
+
+void IsUpOracle::checkURLThread(ComboAddress rem, std::string url, opts_t opts) 
+{
+  setDown(rem, url, opts);
+  for(bool first=true;;first=false) {
+    try {
+      MiniCurl mc;
+      //      cout<<"Checking URL "<<url<<" at "<<rem.toString()<<endl;
+      string content=mc.getURL(url, &rem);
+      if(opts.count("stringmatch") && content.find(opts["stringmatch"]) == string::npos) {
+        //        cout<<"URL "<<url<<" is up at "<<rem.toString()<<", but could not find stringmatch "<<opts["stringmatch"]<<" in page content, setting DOWN"<<endl;
+        setDown(rem, url, opts);
+        goto loop;
+      }
+      if(!upStatus(rem,url))
+        L<<Logger::Warning<<"LUA record monitoring declaring "<<rem.toString()<<" UP for URL "<<url<<"!"<<endl;
+      setUp(rem, url);
+    }
+    catch(std::exception& ne) {
+      if(upStatus(rem,url,opts) || first)
+        L<<Logger::Warning<<"LUA record monitoring declaring "<<rem.toString()<<" DOWN for URL "<<url<<", error: "<<ne.what()<<endl;
+      setDown(rem,url);
+    }
+  loop:;
+    sleep(5);
+  }
+}
+
+
+IsUpOracle g_up;
+namespace {
+template<typename T, typename C>
+bool doCompare(const T& var, const std::string& res, const C& cmp)
+{
+  if(auto country = boost::get<string>(&var)) 
+    return cmp(*country, res);
+
+  auto countries=boost::get<vector<pair<int,string> > >(&var);
+  for(const auto& country : *countries) {
+    if(cmp(country.second, res))
+      return true;
+  }
+  return false;
+}
+}
+static string getGeo(const std::string& ip, GeoIPBackend::GeoIPQueryAttribute qa)
+{
+  try {
+    GeoIPBackend gib;
+    GeoIPLookup gl;
+    string res=gib.queryGeoIP(ip, false, qa, &gl);
+    //    cout<<"Result for "<<ip<<" lookup: "<<res<<endl;
+    if(qa==GeoIPBackend::ASn && boost::starts_with(res, "as"))
+      return res.substr(2);
+    return res;
+  }
+  catch(std::exception& e) {
+    cout<<"Error: "<<e.what()<<endl;
+  }
+  catch(PDNSException& e) {
+    cout<<"Error: "<<e.reason<<endl;
+  }
+  return "";
+}
+
+static bool getLatLon(const std::string& ip, double& lat, double& lon)
+{
+  string inp = getGeo(ip, GeoIPBackend::LatLon);
+  if(inp.empty())
+    return false;
+  lat=atof(inp.c_str());
+  auto pos=inp.find(' ');
+  if(pos != string::npos)
+    lon=atof(inp.c_str() + pos);
+  return true;
+}
+
+std::vector<shared_ptr<DNSRecordContent>> luaSynth(const std::string& code, const DNSName& query, const DNSName& zone, int zoneid, const DNSPacket& dnsp, uint16_t qtype) 
+{
+  cerr<<"Called for "<<query<<", in zone "<<zone<<" for type "<<qtype<<endl;
+  
+  AuthLua4 alua("");
+  std::vector<shared_ptr<DNSRecordContent>> ret;
+  
+  LuaContext& lua = *alua.getLua();
+  lua.writeVariable("qname", query);
+  lua.writeVariable("who", dnsp.getRemote());
+  ComboAddress bestwho;
+  if(dnsp.hasEDNSSubnet()) {
+    lua.writeVariable("ecs-who", dnsp.getRealRemote());
+    bestwho=dnsp.getRealRemote().getNetwork();
+    lua.writeVariable("best-who", dnsp.getRealRemote().getNetwork());
+  }
+  else {
+    lua.writeVariable("ecs-who", dnsp.getRemote());
+    bestwho=dnsp.getRemote();
+  }
+
+  lua.writeFunction("latlon", [&bestwho]() {
+    double lat, lon;
+    getLatLon(bestwho.toString(), lat, lon);
+    return std::to_string(lat)+" "+std::to_string(lon);
+  });
+  
+  lua.writeFunction("closestMagic", [&bestwho,&query](){
+      cout<<query<<endl;
+      vector<ComboAddress> candidates;
+      for(auto l : query.getRawLabels()) {
+        boost::replace_all(l, "-", ".");
+        cout<<l<<endl;
+        try {
+          candidates.emplace_back(l);
+        }
+        catch(...) {
+          break;
+        }
+      }
+      
+      map<double,vector<ComboAddress> > ranked;
+      double wlat=0, wlon=0;
+      getLatLon(bestwho.toString(), wlat, wlon);
+      //        cout<<"bestwho "<<wlat<<", "<<wlon<<endl;
+      vector<string> ret;
+      for(const auto& c : candidates) {
+        double lat=0, lon=0;
+        getLatLon(c.toString(), lat, lon);
+        //          cout<<c.toString()<<": "<<lat<<", "<<lon<<endl;
+        double latdiff = wlat-lat;
+        double londiff = wlon-lon;
+        if(londiff > 180)
+          londiff = 360 - londiff; 
+        double dist2=latdiff*latdiff + londiff*londiff;
+        //          cout<<"    distance: "<<sqrt(dist2) * 40000.0/360<<" km"<<endl; // length of a degree
+        ranked[dist2].push_back(c);
+      }
+      ret.push_back(ranked.begin()->second[random() % ranked.begin()->second.size()].toString());
+      return ret;
+    });
+  
+  
+  lua.writeFunction("ifportup", [&bestwho](int port, const vector<pair<int, string> >& ips, const std::unordered_map<string,string>& options) {
+      vector<ComboAddress> candidates;
+      for(const auto& i : ips) {
+        ComboAddress rem(i.second, port);
+        if(g_up.isUp(rem))
+          candidates.push_back(rem);
+      }
+      vector<string> ret;
+      if(candidates.empty()) {
+        //        cout<<"Everything is down. Returning all of them"<<endl;
+        for(const auto& i : ips) 
+          ret.push_back(i.second);
+      }
+      else {
+        map<double,vector<ComboAddress> > ranked;
+        double wlat=0, wlon=0;
+        getLatLon(bestwho.toString(), wlat, wlon);
+        //        cout<<"bestwho "<<wlat<<", "<<wlon<<endl;
+        for(const auto& c : candidates) {
+          double lat=0, lon=0;
+          getLatLon(c.toString(), lat, lon);
+          //          cout<<c.toString()<<": "<<lat<<", "<<lon<<endl;
+          double latdiff = wlat-lat;
+          double londiff = wlon-lon;
+          if(londiff > 180)
+            londiff = 360 - londiff; 
+          double dist2=latdiff*latdiff + londiff*londiff;
+          //          cout<<"    distance: "<<sqrt(dist2) * 40000.0/360<<" km"<<endl; // length of a degree
+          ranked[dist2].push_back(c);
+        }
+        ret.push_back(ranked.begin()->second[random() % ranked.begin()->second.size()].toString());
+        //        ret.push_back(candidates[random() % candidates.size()].toString());
+      }
+      return ret;
+    });
+
+
+  lua.writeFunction("ifurlup", [](const std::string& url,
+                                  const boost::variant<
+                                  vector<pair<int, string> >,
+                                  vector<pair<int, vector<pair<int, string> > > >
+                                  > & ips, boost::optional<std::unordered_map<string,string>> options) {
+
+      vector<vector<ComboAddress> > candidates;
+      std::unordered_map<string,string> opts;
+      if(options)
+        opts = *options;
+      if(auto simple = boost::get<vector<pair<int,string>>>(&ips)) {
+        vector<ComboAddress> unit;
+        for(const auto& i : *simple) {
+          ComboAddress rem(i.second, 80);
+          unit.push_back(rem);
+        }
+        candidates.push_back(unit);
+      } else {
+        auto units = boost::get<vector<pair<int, vector<pair<int, string> > > >>(ips);
+        for(const auto& u : units) {
+          vector<ComboAddress> unit;
+          for(const auto& c : u.second) {
+            ComboAddress rem(c.second, 80);
+            unit.push_back(rem);
+          }
+          candidates.push_back(unit);
+        }
+      }
+
+      //
+      //      cout<<"Have "<<candidates.size()<<" units of IP addresses: "<<endl;
+      vector<string> ret;
+      for(const auto& unit : candidates) {
+        vector<ComboAddress> available;
+        for(const auto& c : unit)
+          if(g_up.isUp(c, url, opts))
+            available.push_back(c);
+        if(available.empty()) {
+          //  cerr<<"Entire unit is down, trying next one if available"<<endl;
+          continue;
+        }
+        ret.push_back(available[random() % available.size()].toString());
+        return ret;
+      }      
+      //      cerr<<"ALL units are down, returning all IP addresses"<<endl;
+      for(const auto& unit : candidates) {
+        for(const auto& c : unit)
+          ret.push_back(c.toString());
+      }
+
+      return ret;
+                    });
+
+  
+  lua.writeFunction("pickrandom", [](const vector<pair<int, string> >& ips) {
+      return ips[random()%ips.size()].second;
+    });
+
+  // wrandom({ {100, '1.2.3.4'}, {50, '5.4.3.2'}, {1, '192.168.1.0'}})"
+
+  lua.writeFunction("wrandom", [](std::unordered_map<int, std::unordered_map<int, string> > ips) {
+      int sum=0;
+      vector<pair<int, string> > pick;
+      for(auto& i : ips) {
+        sum += atoi(i.second[1].c_str());
+        pick.push_back({sum, i.second[2]});
+      }
+      int r = random() % sum;
+      auto p = upper_bound(pick.begin(), pick.end(),r, [](int r, const decltype(pick)::value_type& a) { return  r < a.first;});
+      return p->second;
+      
+    });
+
+  int counter=0;
+  lua.writeFunction("report", [&counter](string event, boost::optional<string> line){
+      throw std::runtime_error("Script took too long");
+    });
+  lua.executeCode("debug.sethook(report, '', 1000)");
+
+  lua.writeFunction("latlon", [&bestwho]() {
+      return getGeo(bestwho.toString(), GeoIPBackend::LatLon);
+    });
+  
+  typedef const boost::variant<string,vector<pair<int,string> > > combovar_t;
+  lua.writeFunction("continent", [&bestwho](const combovar_t& continent) {
+      string res=getGeo(bestwho.toString(), GeoIPBackend::Continent);
+      return doCompare(continent, res, [](const std::string& a, const std::string& b) {
+          return !strcasecmp(a.c_str(), b.c_str());
+        });
+    });
+
+  lua.writeFunction("asnum", [&bestwho](const combovar_t& asns) {
+      string res=getGeo(bestwho.toString(), GeoIPBackend::ASn);
+      cerr<<"res: "<<res<<endl;
+      return doCompare(asns, res, [](const std::string& a, const std::string& b) {
+          cerr<<a<<", "<<b<<endl;
+          return !strcasecmp(a.c_str(), b.c_str());
+        });
+    });
+  
+  lua.writeFunction("country", [&bestwho](const combovar_t& var) {
+      string res = getGeo(bestwho.toString(), GeoIPBackend::Country2);
+      return doCompare(var, res, [](const std::string& a, const std::string& b) {
+          return !strcasecmp(a.c_str(), b.c_str());
+        });
+       
+    });
+
+  lua.writeFunction("netmask", [bestwho](const vector<pair<int,string>>& ips) {
+      for(const auto& i :ips) {
+        Netmask nm(i.second);
+        if(nm.match(bestwho))
+          return true;
+      }
+      return false;
+    });
+
+  /* {
+       {
+        {'192.168.0.0/16', '10.0.0.0/8'}, 
+        {'192.168.20.20', '192.168.20.21'}
+       },
+       {
+        {'0.0.0.0/0'}, {'192.0.2.1'}
+       }
+     }
+  */  
+  lua.writeFunction("view", [bestwho](const vector<pair<int, vector<pair<int, vector<pair<int, string> > > > > >& in) {
+      for(const auto& rule : in) {
+        const auto& netmasks=rule.second[0].second;
+        const auto& destinations=rule.second[1].second;
+        for(const auto& nmpair : netmasks) {
+          Netmask nm(nmpair.second);
+          if(nm.match(bestwho)) {
+            return destinations[random() % destinations.size()].second;
+          }
+        }
+      }
+      return std::string();
+    }
+    );
+  
+  
+  lua.writeFunction("include", [&lua,zone,zoneid](string record) {
+      try {
+        UeberBackend ub;
+        ub.lookup(QType(QType::LUA), DNSName(record) +zone, 0, zoneid);
+        DNSZoneRecord dr;
+        while(ub.get(dr)) {
+          auto lr = getRR<LUARecordContent>(dr.dr);
+          lua.executeCode(lr->getCode());
+        }
+      }catch(std::exception& e) { cerr<<"Oops: "<<e.what()<<endl; }
+    });
+
+  
+  try {
+    string actual;
+    if(!code.empty() && code[0]!=';')
+      actual = "return ";
+    actual+=code;
+    auto content=lua.executeCode<boost::variant<string, vector<pair<int, string> > > >(actual);
+    //  cout<<"Counter: "<<counter<<endl;
+    vector<string> contents;
+    if(auto str = boost::get<string>(&content))
+      contents.push_back(*str);
+    else
+      for(const auto& c : boost::get<vector<pair<int,string>>>(content))
+        contents.push_back(c.second);
+    
+    for(const auto& content: contents) {
+      if(qtype==QType::TXT)
+        ret.push_back(std::shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(qtype, 1, '"'+content+'"' )));
+      else
+        ret.push_back(std::shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(qtype, 1, content )));
+    }
+  }catch(std::exception &e) {
+    cerr<<"Lua reported: "<<e.what()<<endl;
+  }
+
+  return ret;
+}
index b9c0d7ae9dc33b9434a20556c54804b34a0d54c1..b6223e137b4401f8010058bf448439f838c883c3 100644 (file)
@@ -358,8 +358,24 @@ bool PacketHandler::getBestWildcard(DNSPacket *p, SOAData& sd, const DNSName &ta
       B.lookup(QType(QType::ANY), g_wildcarddnsname+subdomain, p, sd.domain_id);
     }
     while(B.get(rr)) {
-      if(rr.dr.d_type == p->qtype.getCode() || rr.dr.d_type == QType::CNAME || (p->qtype.getCode() == QType::ANY && rr.dr.d_type != QType::RRSIG))
+      if(rr.dr.d_type == QType::LUA) {
+        DLOG(L<<"Have a wildcard LUA match"<<endl);
+        auto rec=getRR<LUARecordContent>(rr.dr);
+        if(rec->d_type == QType::CNAME || rec->d_type == p->qtype.getCode()) {
+          //    noCache=true;
+          DLOG(L<<"Executing Lua: '"<<rec->getCode()<<"'"<<endl);
+          auto recvec=luaSynth(rec->getCode(), target, sd.qname, sd.domain_id, *p, rec->d_type);
+          for(const auto& r : recvec) {
+            rr.dr.d_type = rec->d_type; // might be CNAME
+            rr.dr.d_content = r;
+            rr.scopeMask = 32; // XXX or 128 - needs to disable cache in any case
+            ret->push_back(rr);
+          }
+        }
+      }
+      else if(rr.dr.d_type == p->qtype.getCode() || rr.dr.d_type == QType::CNAME || (p->qtype.getCode() == QType::ANY && rr.dr.d_type != QType::RRSIG))
         ret->push_back(rr);
+      
       wildcard=g_wildcarddnsname+subdomain;
       haveSomething=true;
     }