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::
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'}) " )
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) " )
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
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.
+
#include <mutex>
#include "minicurl.hh"
#include "ueberbackend.hh"
+#include <boost/format.hpp>
#include "../../modules/geoipbackend/geoipbackend.hh"
class IsUpOracle
{
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);
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;
getLatLon(bestwho.toString(), lat, lon);
return std::to_string(lat)+" "+std::to_string(lon);
});
+
lua.writeFunction("closestMagic", [&bestwho,&query](){
cout<<query<<endl;
}
}
- 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);
});
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;
});
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;
// 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");
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());
});
});