]> granicus.if.org Git - pdns/commitdiff
documentation + cleanup
authorbert hubert <bert.hubert@netherlabs.nl>
Fri, 8 Dec 2017 22:25:33 +0000 (23:25 +0100)
committerbert hubert <bert.hubert@netherlabs.nl>
Fri, 8 Dec 2017 22:25:33 +0000 (23:25 +0100)
docs/lua-records.rst
pdns/lua-record.cc

index 76666d2d9ae0a94e8485787da340d38ae074cdf5..af023d8b23cd02fd2d1502d790fe1725f57720b9 100644 (file)
@@ -29,19 +29,28 @@ dynamic records can do.
 
 Here is a very basic example::
 
-     www       IN      LUA     A       "return ifportup(443, {'192.0.2.1', '192.0.2.2'})"
+     www    IN    LUA    A    "ifportup(443, {'192.0.2.1', '192.0.2.2'})"
 
 This turns the 'www' name within a zone into a magical record that will
 randomly return 192.0.2.1 or 192.0.2.2, as long as both of these IP
 addresses listen on port 443. 
 
 If either IP address stops listening, only the other address will be
-returned. If all IP addresses are down a random one is returned anyhow.
+returned. If all IP addresses are down, all candidates are returned.
 
 Because DNS queries require rapid answers, server availability is not checked
 synchronously. In the background, a process periodically determines if IP
 addresses mentioned in availability rules are, in fact, available.
 
+Another example::
+  
+    www    IN    LUA    A    "closest({'192.0.2.1','192.0.2.2','198.51.100.1'})"
+
+This uses the GeoIP backend to find indications of the geographical location of
+the requestor and the listed IP addresses. It will return with one of the closest
+addresses.
+
+
 Record format
 -------------
 .. note::
@@ -63,7 +72,7 @@ More powerful example
 
 A more powerful example::
 
-    west    IN    LUA    A    ( "return ifurlup('https://www.lua.org/',           "
+    west    IN    LUA    A    ( "ifurlup('https://www.lua.org/',                  "
                                 "{{'192.0.2.1', '192.0.2.2'}, {'198.51.100.1'}},  "
                                 "{stringmatch='Programming in Lua'})              " )
 
@@ -95,22 +104,25 @@ outside of Europe will hit 198.51.100.1 as long as it is available, and the
 
 Advanced topics
 ---------------
+By default, LUA records are executed with 'return ' prefixed to them. This saves
+a lof of typing for common cases. To run actual Lua scripts, start a record with a ';'
+which indicates no 'return ' should be prepended.
 
 To keep records more concise and readable, configuration can be stored in
 separate records. The full example from above can also be written as::
 
-    config    IN    LUA    LUA ("settings={stringmatch='Programming in Lua'}  "
+    config    IN    LUA    LUA (";settings={stringmatch='Programming in Lua'}  "
                                 "EUips={'192.0.2.1', '192.0.2.2'}             "
                                 "USAips={'198.51.100.1'}                      ")
 
-    www       IN    LUA    CNAME ( "if(continent('EU')) then return 'west.powerdns.org' "
+    www       IN    LUA    CNAME ( ";if(continent('EU')) then return 'west.powerdns.org' "
                                    "else return 'usa.powerdns.org' end" )
 
-    usa       IN    LUA    A    ( "include(config)                               "
+    usa       IN    LUA    A    ( ";include(config)                               "
                                   "return ifurlup('https://www.lua.org/',        "
                                   "{USAips, EUips}, settings)                    " )
 
-    west      IN    LUA    A    ( "include(config)                               "
+    west      IN    LUA    A    ( ";include(config)                               "
                                   "return ifurlup('https://www.lua.org/',        "
                                   "{EUips, USAips}, settings)                    " )
 
@@ -190,6 +202,17 @@ This will return IP address 192.168.1.54 for queries coming from
 
 This function also works for CNAME or TXT records.
 
+``whashed({{weight, 'ip1'}, {weight, 'ip2'}})``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Based on the hash of ``bestwho``, returns an IP address from the list
+supplied, as weighted by the various ``weight`` parameters.
+
+Because of the hash, the same client keeps getting the same answer, but
+given sufficient clients, the load is still spread according to the weight
+factors.
+
+Performs no uptime checking.
+
 ``wrandom({{weight, 'ip1'}, {weight, 'ip2'}})``
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Returns a random IP address from the list supplied, as weighted by the
@@ -215,7 +238,13 @@ they do not need to see.
 A non-supporting DNS server will also serve a zone with LUA records, but
 they will not function.
 
+.. note::
+  Under NO circumstances serve LUA records from zones from untrusted sources!
+  LUA records will be able to bring down your system and possible take over
+  control of it. 
+
 LUA records can be DNSSEC signed, but because they are dynamic, it is not
 possible to combine pre-signed DNSSEC zone and LUA records. In other words,
 the signing key must be available on the server creating answers based on
 LUA records.
+
index 9c166829769de43a74dfb0d7a476cac300ff3bae..282f181bd528af6e81aac3560c41db790afcbdce 100644 (file)
@@ -5,6 +5,7 @@
 #include <mutex>
 #include "minicurl.hh"
 #include "ueberbackend.hh"
+#include <boost/format.hpp>
 #include "../../modules/geoipbackend/geoipbackend.hh"
 class IsUpOracle
 {
@@ -196,6 +197,33 @@ static string getGeo(const std::string& ip, GeoIPBackend::GeoIPQueryAttribute qa
   return "";
 }
 
+static ComboAddress wrandom(vector<pair<int,ComboAddress> >& wips)
+{
+  int sum=0;
+  vector<pair<int, ComboAddress> > pick;
+  for(auto& i : wips) {
+    sum += i.first;
+    pick.push_back({sum, i.second});
+  }
+  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;
+}
+
+static ComboAddress whashed(const ComboAddress& bestwho, vector<pair<int,ComboAddress> >& wips)
+{
+  int sum=0;
+  vector<pair<int, ComboAddress> > pick;
+  for(auto& i : wips) {
+    sum += i.first;
+    pick.push_back({sum, i.second});
+  }
+  ComboAddress::addressOnlyHash aoh;
+  int r = aoh(bestwho) % 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;
+}
+
 static bool getLatLon(const std::string& ip, double& lat, double& lon)
 {
   string inp = getGeo(ip, GeoIPBackend::LatLon);
@@ -208,9 +236,34 @@ static bool getLatLon(const std::string& ip, double& lat, double& lon)
   return true;
 }
 
+
+static ComboAddress closest(const ComboAddress& bestwho, vector<ComboAddress>& wips)
+{
+  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 : wips) {
+    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);
+  }
+  return ranked.begin()->second[random() % ranked.begin()->second.size()];
+}
+
+
+
 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;
+  //  cerr<<"Called for "<<query<<", in zone "<<zone<<" for type "<<qtype<<endl;
   
   AuthLua4 alua("");
   std::vector<shared_ptr<DNSRecordContent>> ret;
@@ -234,6 +287,7 @@ std::vector<shared_ptr<DNSRecordContent>> luaSynth(const std::string& code, cons
     getLatLon(bestwho.toString(), lat, lon);
     return std::to_string(lat)+" "+std::to_string(lon);
   });
+
   
   lua.writeFunction("closestMagic", [&bestwho,&query](){
       cout<<query<<endl;
@@ -249,25 +303,7 @@ std::vector<shared_ptr<DNSRecordContent>> luaSynth(const std::string& code, cons
         }
       }
       
-      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;
+      return closest(bestwho, candidates);
     });
   
   
@@ -285,24 +321,8 @@ std::vector<shared_ptr<DNSRecordContent>> luaSynth(const std::string& code, cons
           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());
+        auto res=closest(bestwho, candidates);
+        ret.push_back(res.toString());
       }
       return ret;
     });
@@ -361,6 +381,11 @@ std::vector<shared_ptr<DNSRecordContent>> luaSynth(const std::string& code, cons
       return ret;
                     });
 
+
+
+  /* idea: we have policies on vectors of ComboAddresses, like
+     random, wrandom, whashed, closest. In C++ this is ComboAddress in,
+     ComboAddress out. In Lua, vector string in, string out */
   
   lua.writeFunction("pickrandom", [](const vector<pair<int, string> >& ips) {
       return ips[random()%ips.size()].second;
@@ -369,18 +394,33 @@ std::vector<shared_ptr<DNSRecordContent>> luaSynth(const std::string& code, cons
   // 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;
+      vector<pair<int,ComboAddress> > conv;
+      for(auto& i : ips) 
+        conv.emplace_back(atoi(i.second[1].c_str()), ComboAddress(i.second[2]));
+      
+      return wrandom(conv).toString();
+    });
+
+  lua.writeFunction("whashed", [&bestwho](std::unordered_map<int, std::unordered_map<int, string> > ips) {
+      vector<pair<int,ComboAddress> > conv;
+      for(auto& i : ips) 
+        conv.emplace_back(atoi(i.second[1].c_str()), ComboAddress(i.second[2]));
+      
+      return whashed(bestwho, conv).toString();
+      
+    });
+
+
+  lua.writeFunction("closest", [&bestwho](std::unordered_map<int, std::unordered_map<int, string> > ips) {
+      vector<ComboAddress > conv;
+      for(auto& i : ips) 
+        conv.emplace_back(i.second[2]);
+      
+      return closest(bestwho, conv).toString();
       
     });
 
+  
   int counter=0;
   lua.writeFunction("report", [&counter](string event, boost::optional<string> line){
       throw std::runtime_error("Script took too long");
@@ -401,9 +441,7 @@ std::vector<shared_ptr<DNSRecordContent>> luaSynth(const std::string& code, cons
 
   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());
         });
     });