};
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) {
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;
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) {
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;
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
Country,
Country2,
Name,
- Region
+ Region,
+ LatLon
};
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);
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);
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 \
+#include "config.h"
+#if defined(HAVE_LUA)
+#include "ext/luawrapper/include/LuaContext.hpp"
+#endif
#include "lua-auth4.hh"
#include "stubresolver.hh"
#include <fstream>
#include "sstuff.hh"
#include <thread>
#include <mutex>
-#include "minicurl.hh"
+
#include "ueberbackend.hh"
+
#if !defined(HAVE_LUA)
AuthLua4::AuthLua4(const std::string& fname) { }
#else
-#undef L
-#include "ext/luawrapper/include/LuaContext.hpp"
-
LuaContext* AuthLua4::getLua()
{
return d_lw.get();
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
--- /dev/null
+#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;
+}
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;
}