]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/lua-record.cc
1 #include "ext/luawrapper/include/LuaContext.hpp"
2 #include "lua-auth4.hh"
7 #include "ueberbackend.hh"
8 #include <boost/format.hpp>
9 #include "dnsrecords.hh"
11 #include "../modules/geoipbackend/geoipinterface.hh" // only for the enum
14 block AXFR unless TSIG, or override
18 check the wildcard 'no cache' stuff, we may get it wrong
20 ponder ECS scopemask setting
22 ponder netmask tree from file for huge number of netmasks
24 unify ifupurl/ifupport
25 add attribute for certificate check
26 add list of current monitors
29 pool of UeberBackends?
34 extern int g_luaRecordExecLimit
;
36 using iplist_t
= vector
<pair
<int, string
> >;
37 using wiplist_t
= std::unordered_map
<int, string
>;
38 using ipunitlist_t
= vector
<pair
<int, iplist_t
> >;
39 using opts_t
= std::unordered_map
<string
,string
>;
49 bool operator<(const CheckDesc
& rhs
) const
51 std::map
<string
,string
> oopts
, rhsoopts
;
52 for(const auto& m
: opts
)
53 oopts
[m
.first
]=m
.second
;
54 for(const auto& m
: rhs
.opts
)
55 rhsoopts
[m
.first
]=m
.second
;
57 return std::make_tuple(rem
, url
, oopts
) <
58 std::make_tuple(rhs
.rem
, rhs
.url
, rhsoopts
);
62 bool isUp(const ComboAddress
& remote
, const opts_t
& opts
);
63 bool isUp(const ComboAddress
& remote
, const std::string
& url
, const opts_t
& opts
);
64 bool isUp(const CheckDesc
& cd
);
67 void checkURLThread(ComboAddress rem
, std::string url
, const opts_t
& opts
);
68 void checkTCPThread(ComboAddress rem
, const opts_t
& opts
);
76 typedef map
<CheckDesc
, Checker
> statuses_t
;
77 statuses_t d_statuses
;
81 void setStatus(const CheckDesc
& cd
, bool status
)
83 std::lock_guard
<std::mutex
> l(d_mutex
);
84 d_statuses
[cd
].status
=status
;
87 void setDown(const ComboAddress
& rem
, const std::string
& url
=std::string(), const opts_t
& opts
= opts_t())
89 CheckDesc cd
{rem
, url
, opts
};
93 void setUp(const ComboAddress
& rem
, const std::string
& url
=std::string(), const opts_t
& opts
= opts_t())
95 CheckDesc cd
{rem
, url
, opts
};
100 void setDown(const CheckDesc
& cd
)
102 setStatus(cd
, false);
105 void setUp(const CheckDesc
& cd
)
110 bool upStatus(const ComboAddress
& rem
, const std::string
& url
=std::string(), const opts_t
& opts
= opts_t())
112 CheckDesc cd
{rem
, url
, opts
};
113 std::lock_guard
<std::mutex
> l(d_mutex
);
114 return d_statuses
[cd
].status
;
117 statuses_t
getStatus()
119 std::lock_guard
<std::mutex
> l(d_mutex
);
125 bool IsUpOracle::isUp(const CheckDesc
& cd
)
127 std::lock_guard
<std::mutex
> l(d_mutex
);
128 auto iter
= d_statuses
.find(cd
);
129 if(iter
== d_statuses
.end()) {
130 std::thread
* checker
= new std::thread(&IsUpOracle::checkTCPThread
, this, cd
.rem
, cd
.opts
);
131 d_statuses
[cd
]=Checker
{checker
, false};
134 return iter
->second
.status
;
138 bool IsUpOracle::isUp(const ComboAddress
& remote
, const opts_t
& opts
)
140 CheckDesc cd
{remote
, "", opts
};
144 bool IsUpOracle::isUp(const ComboAddress
& remote
, const std::string
& url
, const opts_t
& opts
)
146 CheckDesc cd
{remote
, url
, opts
};
147 std::lock_guard
<std::mutex
> l(d_mutex
);
148 auto iter
= d_statuses
.find(cd
);
149 if(iter
== d_statuses
.end()) {
150 // g_log<<Logger::Warning<<"Launching HTTP(s) status checker for "<<remote.toStringWithPort()<<" and URL "<<url<<endl;
151 std::thread
* checker
= new std::thread(&IsUpOracle::checkURLThread
, this, remote
, url
, opts
);
152 d_statuses
[cd
]=Checker
{checker
, false};
156 return iter
->second
.status
;
159 void IsUpOracle::checkTCPThread(ComboAddress rem
, const opts_t
& opts
)
161 CheckDesc cd
{rem
, "", opts
};
163 for(bool first
=true;;first
=false) {
165 Socket
s(rem
.sin4
.sin_family
, SOCK_STREAM
);
168 if(opts
.count("source")) {
169 src
=ComboAddress(opts
.at("source"));
174 g_log
<<Logger::Warning
<<"Lua record monitoring declaring TCP/IP "<<rem
.toStringWithPort()<<" ";
175 if(opts
.count("source"))
176 g_log
<<"(source "<<src
.toString()<<") ";
181 catch(NetworkError
& ne
) {
182 if(isUp(rem
, opts
) || first
)
183 g_log
<<Logger::Warning
<<"Lua record monitoring declaring TCP/IP "<<rem
.toStringWithPort()<<" DOWN: "<<ne
.what()<<endl
;
191 void IsUpOracle::checkURLThread(ComboAddress rem
, std::string url
, const opts_t
& opts
)
193 setDown(rem
, url
, opts
);
194 for(bool first
=true;;first
=false) {
199 if(opts
.count("source")) {
200 ComboAddress
src(opts
.at("source"));
201 content
=mc
.getURL(url
, &rem
, &src
);
204 content
=mc
.getURL(url
, &rem
);
206 if(opts
.count("stringmatch") && content
.find(opts
.at("stringmatch")) == string::npos
) {
207 throw std::runtime_error(boost::str(boost::format("unable to match content with `%s`") % opts
.at("stringmatch")));
209 if(!upStatus(rem
,url
,opts
))
210 g_log
<<Logger::Warning
<<"LUA record monitoring declaring "<<rem
.toString()<<" UP for URL "<<url
<<"!"<<endl
;
211 setUp(rem
, url
,opts
);
213 catch(std::exception
& ne
) {
214 if(upStatus(rem
,url
,opts
) || first
)
215 g_log
<<Logger::Warning
<<"LUA record monitoring declaring "<<rem
.toString()<<" DOWN for URL "<<url
<<", error: "<<ne
.what()<<endl
;
216 setDown(rem
,url
,opts
);
225 template<typename T
, typename C
>
226 bool doCompare(const T
& var
, const std::string
& res
, const C
& cmp
)
228 if(auto country
= boost::get
<string
>(&var
))
229 return cmp(*country
, res
);
231 auto countries
=boost::get
<vector
<pair
<int,string
> > >(&var
);
232 for(const auto& country
: *countries
) {
233 if(cmp(country
.second
, res
))
241 std::string
getGeo(const std::string
& ip
, GeoIPInterface::GeoIPQueryAttribute qa
)
243 static bool initialized
;
244 extern std::function
<std::string(const std::string
& ip
, int)> g_getGeo
;
247 g_log
<<Logger::Error
<<"LUA Record attempted to use GeoIPBackend functionality, but backend not launched"<<endl
;
253 return g_getGeo(ip
, (int)qa
);
256 static ComboAddress
pickrandom(const vector
<ComboAddress
>& ips
)
259 throw std::invalid_argument("The IP list cannot be empty");
261 return ips
[random() % ips
.size()];
264 static ComboAddress
hashed(const ComboAddress
& who
, const vector
<ComboAddress
>& ips
)
267 throw std::invalid_argument("The IP list cannot be empty");
269 ComboAddress::addressOnlyHash aoh
;
270 return ips
[aoh(who
) % ips
.size()];
274 static ComboAddress
pickwrandom(const vector
<pair
<int,ComboAddress
> >& wips
)
277 throw std::invalid_argument("The IP list cannot be empty");
280 vector
<pair
<int, ComboAddress
> > pick
;
281 for(auto& i
: wips
) {
283 pick
.push_back({sum
, i
.second
});
285 int r
= random() % sum
;
286 auto p
= upper_bound(pick
.begin(), pick
.end(),r
, [](int r
, const decltype(pick
)::value_type
& a
) { return r
< a
.first
;});
290 static ComboAddress
pickwhashed(const ComboAddress
& bestwho
, vector
<pair
<int,ComboAddress
> >& wips
)
293 return ComboAddress();
296 vector
<pair
<int, ComboAddress
> > pick
;
297 for(auto& i
: wips
) {
299 pick
.push_back({sum
, i
.second
});
301 ComboAddress::addressOnlyHash aoh
;
302 int r
= aoh(bestwho
) % sum
;
303 auto p
= upper_bound(pick
.begin(), pick
.end(),r
, [](int r
, const decltype(pick
)::value_type
& a
) { return r
< a
.first
;});
307 static bool getLatLon(const std::string
& ip
, double& lat
, double& lon
)
309 string inp
= getGeo(ip
, GeoIPInterface::Location
);
312 lat
=atof(inp
.c_str());
313 auto pos
=inp
.find(' ');
314 if(pos
!= string::npos
)
315 lon
=atof(inp
.c_str() + pos
);
319 static bool getLatLon(const std::string
& ip
, string
& loc
)
321 int latdeg
, latmin
, londeg
, lonmin
;
322 double latsec
, lonsec
;
323 char lathem
='X', lonhem
='X';
326 if(!getLatLon(ip
, lat
, lon
))
347 >>> min = int((R - int(R)) * 60.0)
348 >>> sec = (((R - int(R)) * 60.0) - min) * 60.0
349 >>> print("{}º {}' {}\"".format(deg, min, sec))
354 latmin
= (lat
- latdeg
)*60.0;
355 latsec
= (((lat
- latdeg
)*60.0) - latmin
)*60.0;
358 lonmin
= (lon
- londeg
)*60.0;
359 lonsec
= (((lon
- londeg
)*60.0) - lonmin
)*60.0;
361 // 51 59 00.000 N 5 55 00.000 E 4.00m 1.00m 10000.00m 10.00m
363 boost::format
fmt("%d %d %d %c %d %d %d %c 0.00m 1.00m 10000.00m 10.00m");
365 loc
= (fmt
% latdeg
% latmin
% latsec
% lathem
% londeg
% lonmin
% lonsec
% lonhem
).str();
369 static ComboAddress
pickclosest(const ComboAddress
& bestwho
, const vector
<ComboAddress
>& wips
)
372 throw std::invalid_argument("The IP list cannot be empty");
374 map
<double,vector
<ComboAddress
> > ranked
;
375 double wlat
=0, wlon
=0;
376 getLatLon(bestwho
.toString(), wlat
, wlon
);
377 // cout<<"bestwho "<<wlat<<", "<<wlon<<endl;
379 g_log
<<Logger::Debug
<< __FILE__
<< ":" << __LINE__
<< " " << __func__
<< " wips.size()="<< wips
.size() <<endl
;
380 for(const auto& c
: wips
) {
382 getLatLon(c
.toString(), lat
, lon
);
383 // cout<<c.toString()<<": "<<lat<<", "<<lon<<endl;
384 double latdiff
= wlat
-lat
;
385 double londiff
= wlon
-lon
;
387 londiff
= 360 - londiff
;
388 double dist2
=latdiff
*latdiff
+ londiff
*londiff
;
389 // cout<<" distance: "<<sqrt(dist2) * 40000.0/360<<" km"<<endl; // length of a degree
390 ranked
[dist2
].push_back(c
);
392 g_log
<<Logger::Debug
<< __FILE__
<< ":" << __LINE__
<< " " << __func__
<< " ranked.size()="<< ranked
.size()<<endl
;
393 ranked
.begin()->second
.size();
394 g_log
<<Logger::Debug
<< __FILE__
<< ":" << __LINE__
<< " " << __func__
<< " ranked.size()="<< ranked
.size()<<endl
;
395 return ranked
.begin()->second
[random() % ranked
.begin()->second
.size()];
398 static std::vector
<DNSZoneRecord
> lookup(const DNSName
& name
, uint16_t qtype
, int zoneid
)
400 static UeberBackend ub
;
401 static std::mutex mut
;
402 std::lock_guard
<std::mutex
> lock(mut
);
403 ub
.lookup(QType(qtype
), name
, nullptr, zoneid
);
405 vector
<DNSZoneRecord
> ret
;
412 static ComboAddress
useSelector(const boost::optional
<std::unordered_map
<string
, string
>>& options
, const ComboAddress
& bestwho
, const vector
<ComboAddress
>& candidates
)
414 string selector
="random";
416 if(options
->count("selector"))
417 selector
=options
->find("selector")->second
;
420 if(selector
=="random")
421 return pickrandom(candidates
);
422 else if(selector
=="pickclosest")
423 return pickclosest(bestwho
, candidates
);
424 else if(selector
=="hashed")
425 return hashed(bestwho
, candidates
);
427 g_log
<<Logger::Warning
<<"LUA Record called with unknown selector '"<<selector
<<"'"<<endl
;
428 return pickrandom(candidates
);
431 static vector
<ComboAddress
> convIplist(const iplist_t
& src
)
433 vector
<ComboAddress
> ret
;
435 for(const auto& ip
: src
)
436 ret
.emplace_back(ip
.second
);
441 static vector
<pair
<int, ComboAddress
> > convWIplist(std::unordered_map
<int, wiplist_t
> src
)
443 vector
<pair
<int,ComboAddress
> > ret
;
445 for(const auto& i
: src
)
446 ret
.emplace_back(atoi(i
.second
.at(1).c_str()), ComboAddress(i
.second
.at(2)));
451 std::vector
<shared_ptr
<DNSRecordContent
>> luaSynth(const std::string
& code
, const DNSName
& query
, const DNSName
& zone
, int zoneid
, const DNSPacket
& dnsp
, uint16_t qtype
)
453 // cerr<<"Called for "<<query<<", in zone "<<zone<<" for type "<<qtype<<endl;
454 // cerr<<"Code: '"<<code<<"'"<<endl;
459 std::vector
<shared_ptr
<DNSRecordContent
>> ret
;
461 LuaContext
& lua
= *alua
.getLua();
462 lua
.writeVariable("qname", query
);
463 lua
.writeVariable("who", dnsp
.getRemote());
464 lua
.writeVariable("dh", (dnsheader
*)&dnsp
.d
);
465 lua
.writeVariable("dnssecOK", dnsp
.d_dnssecOk
);
466 lua
.writeVariable("tcp", dnsp
.d_tcp
);
467 lua
.writeVariable("ednsPKTSize", dnsp
.d_ednsRawPacketSizeLimit
);
468 ComboAddress bestwho
;
469 if(dnsp
.hasEDNSSubnet()) {
470 lua
.writeVariable("ecswho", dnsp
.getRealRemote());
471 bestwho
=dnsp
.getRealRemote().getNetwork();
474 bestwho
=dnsp
.getRemote();
477 lua
.writeVariable("bestwho", bestwho
);
479 lua
.writeFunction("latlon", [&bestwho
]() {
481 getLatLon(bestwho
.toString(), lat
, lon
);
482 return std::to_string(lat
)+" "+std::to_string(lon
);
485 lua
.writeFunction("latlonloc", [&bestwho
]() {
487 getLatLon(bestwho
.toString(), loc
);
492 lua
.writeFunction("closestMagic", [&bestwho
,&query
]() {
493 vector
<ComboAddress
> candidates
;
494 for(auto l
: query
.getRawLabels()) {
495 boost::replace_all(l
, "-", ".");
497 candidates
.emplace_back(l
);
498 } catch (const PDNSException
& e
) {
499 // we want the reason to be reported by the lua wrapper
500 throw std::invalid_argument(e
.reason
);
503 return pickclosest(bestwho
, candidates
).toString();
506 lua
.writeFunction("latlonMagic", [&query
](){
507 auto labels
= query
.getRawLabels();
509 return std::string("unknown");
511 getLatLon(labels
[3]+"."+labels
[2]+"."+labels
[1]+"."+labels
[0], lat
, lon
);
512 return std::to_string(lat
)+" "+std::to_string(lon
);
516 lua
.writeFunction("createReverse", [&bestwho
,&query
,&zone
](string suffix
, boost::optional
<std::unordered_map
<string
,string
>> e
){
518 auto labels
= query
.getRawLabels();
520 return std::string("unknown");
522 vector
<ComboAddress
> candidates
;
524 // exceptions are relative to zone
525 // so, query comes in for 4.3.2.1.in-addr.arpa, zone is called 2.1.in-addr.arpa
526 // e["1.2.3.4"]="bert.powerdns.com" - should match, easy enough to do
527 // the issue is with classless delegation..
529 ComboAddress
req(labels
[3]+"."+labels
[2]+"."+labels
[1]+"."+labels
[0], 0);
530 const auto& uom
= *e
;
531 for(const auto& c
: uom
)
532 if(ComboAddress(c
.first
, 0) == req
)
537 boost::format
fmt(suffix
);
538 fmt
.exceptions( boost::io::all_error_bits
^ ( boost::io::too_many_args_bit
| boost::io::too_few_args_bit
) );
539 fmt
% labels
[3] % labels
[2] % labels
[1] % labels
[0];
541 fmt
% (labels
[3]+"-"+labels
[2]+"-"+labels
[1]+"-"+labels
[0]);
543 boost::format
fmt2("%02x%02x%02x%02x");
544 for(int i
=3; i
>=0; --i
)
545 fmt2
% atoi(labels
[i
].c_str());
551 catch(std::exception
& e
) {
552 g_log
<<Logger::Error
<<"error: "<<e
.what()<<endl
;
554 return std::string("error");
557 lua
.writeFunction("createForward", [&zone
, &query
]() {
558 DNSName rel
=query
.makeRelative(zone
);
559 auto parts
= rel
.getRawLabels();
561 return parts
[0]+"."+parts
[1]+"."+parts
[2]+"."+parts
[3];
562 if(parts
.size()==1) {
563 // either hex string, or 12-13-14-15
564 // cout<<parts[0]<<endl;
566 if(sscanf(parts
[0].c_str()+2, "%02x%02x%02x%02x", &x1
, &x2
, &x3
, &x4
)==4) {
567 return std::to_string(x1
)+"."+std::to_string(x2
)+"."+std::to_string(x3
)+"."+std::to_string(x4
);
572 return std::string("0.0.0.0");
575 lua
.writeFunction("createForward6", [&query
,&zone
]() {
576 DNSName rel
=query
.makeRelative(zone
);
577 auto parts
= rel
.getRawLabels();
578 if(parts
.size()==8) {
580 for(int i
=0; i
<8; ++i
) {
585 ComboAddress
ca(tot
);
586 return ca
.toString();
588 else if(parts
.size()==1) {
589 boost::replace_all(parts
[0],"-",":");
590 ComboAddress
ca(parts
[0]);
591 return ca
.toString();
594 return std::string("::");
598 lua
.writeFunction("createReverse6", [&bestwho
,&query
,&zone
](string suffix
, boost::optional
<std::unordered_map
<string
,string
>> e
){
599 vector
<ComboAddress
> candidates
;
602 auto labels
= query
.getRawLabels();
604 return std::string("unknown");
605 boost::format
fmt(suffix
);
606 fmt
.exceptions( boost::io::all_error_bits
^ ( boost::io::too_many_args_bit
| boost::io::too_few_args_bit
) );
610 vector
<string
> quads
;
611 for(int i
=0; i
<8; ++i
) {
615 for(int j
=0; j
<4; ++j
) {
616 quad
.append(1, labels
[31-i
*4-j
][0]);
617 together
+= labels
[31-i
*4-j
][0];
619 quads
.push_back(quad
);
621 ComboAddress
ip6(together
,0);
625 for(const auto& addr
: addrs
) {
626 // this makes sure we catch all forms of the address
627 if(ComboAddress(addr
.first
,0)==ip6
)
632 string dashed
=ip6
.toString();
633 boost::replace_all(dashed
, ":", "-");
635 for(int i
=31; i
>=0; --i
)
639 for(const auto& quad
: quads
)
644 catch(std::exception
& e
) {
645 g_log
<<Logger::Error
<<"LUA Record xception: "<<e
.what()<<endl
;
647 catch(PDNSException
& e
) {
648 g_log
<<Logger::Error
<<"LUA Record exception: "<<e
.reason
<<endl
;
650 return std::string("unknown");
655 * Simplistic test to see if an IP address listens on a certain port
656 * Will return a single IP address from the set of available IP addresses. If
657 * no IP address is available, will return a random element of the set of
658 * addresses suppplied for testing.
660 * @example ifportup(443, { '1.2.3.4', '5.4.3.2' })"
662 lua
.writeFunction("ifportup", [&bestwho
](int port
, const vector
<pair
<int, string
> >& ips
, const boost::optional
<std::unordered_map
<string
,string
>> options
) {
663 vector
<ComboAddress
> candidates
, unavailables
;
665 vector
<ComboAddress
> conv
;
669 for(const auto& i
: ips
) {
670 ComboAddress
rem(i
.second
, port
);
671 if(g_up
.isUp(rem
, opts
)) {
672 candidates
.push_back(rem
);
675 unavailables
.push_back(rem
);
678 if(candidates
.empty()) {
679 // if no IP is available, use selector on the whole set
680 candidates
= std::move(unavailables
);
682 ComboAddress res
=useSelector(options
, bestwho
, candidates
);
684 return res
.toString();
687 lua
.writeFunction("ifurlup", [&bestwho
](const std::string
& url
,
688 const boost::variant
<iplist_t
, ipunitlist_t
>& ips
,
689 boost::optional
<opts_t
> options
) {
691 vector
<vector
<ComboAddress
> > candidates
;
695 if(auto simple
= boost::get
<iplist_t
>(&ips
)) {
696 vector
<ComboAddress
> unit
= convIplist(*simple
);
697 candidates
.push_back(unit
);
699 auto units
= boost::get
<ipunitlist_t
>(ips
);
700 for(const auto& u
: units
) {
701 vector
<ComboAddress
> unit
= convIplist(u
.second
);
702 candidates
.push_back(unit
);
706 for(const auto& unit
: candidates
) {
707 vector
<ComboAddress
> available
;
708 for(const auto& c
: unit
) {
709 if(g_up
.isUp(c
, url
, opts
)) {
710 available
.push_back(c
);
713 if(!available
.empty()) {
714 ComboAddress res
=useSelector(options
, bestwho
, available
);
716 return res
.toString();
720 // All units down, return a single, random record
721 vector
<ComboAddress
> ret
{};
722 for(const auto& unit
: candidates
) {
723 ret
.insert(ret
.end(), unit
.begin(), unit
.end());
726 return pickrandom(ret
).toString();
730 /* idea: we have policies on vectors of ComboAddresses, like
731 random, pickwrandom, pickwhashed, pickclosest. In C++ this is ComboAddress in,
732 ComboAddress out. In Lua, vector string in, string out */
735 * Returns a random IP address from the supplied list
736 * @example pickrandom({ '1.2.3.4', '5.4.3.2' })"
738 lua
.writeFunction("pickrandom", [](const iplist_t
& ips
) {
739 vector
<ComboAddress
> conv
= convIplist(ips
);
741 return pickrandom(conv
).toString();
746 * Returns a random IP address from the supplied list, as weighted by the
747 * various ``weight`` parameters
748 * @example pickwrandom({ {100, '1.2.3.4'}, {50, '5.4.3.2'}, {1, '192.168.1.0'} })
750 lua
.writeFunction("pickwrandom", [](std::unordered_map
<int, wiplist_t
> ips
) {
751 vector
<pair
<int,ComboAddress
> > conv
= convWIplist(ips
);
753 return pickwrandom(conv
).toString();
757 * Based on the hash of `bestwho`, returns an IP address from the list
758 * supplied, as weighted by the various `weight` parameters
759 * @example pickwhashed({ {15, '1.2.3.4'}, {50, '5.4.3.2'} })
761 lua
.writeFunction("pickwhashed", [&bestwho
](std::unordered_map
<int, wiplist_t
> ips
) {
762 vector
<pair
<int,ComboAddress
> > conv
;
765 conv
.emplace_back(atoi(i
.second
[1].c_str()), ComboAddress(i
.second
[2]));
767 return pickwhashed(bestwho
, conv
).toString();
771 lua
.writeFunction("pickclosest", [&bestwho
](const iplist_t
& ips
) {
772 vector
<ComboAddress
> conv
= convIplist(ips
);
774 return pickclosest(bestwho
, conv
).toString();
780 lua
.writeFunction("report", [&counter
](string event
, boost::optional
<string
> line
){
781 throw std::runtime_error("Script took too long");
783 if (g_luaRecordExecLimit
> 0) {
784 lua
.executeCode(boost::str(boost::format("debug.sethook(report, '', %d)") % g_luaRecordExecLimit
));
787 // TODO: make this better. Accept netmask/CA objects; provide names for the attr constants
788 lua
.writeFunction("geoiplookup", [](const string
&ip
, const GeoIPInterface::GeoIPQueryAttribute attr
) {
789 return getGeo(ip
, attr
);
792 typedef const boost::variant
<string
,vector
<pair
<int,string
> > > combovar_t
;
793 lua
.writeFunction("continent", [&bestwho
](const combovar_t
& continent
) {
794 string res
=getGeo(bestwho
.toString(), GeoIPInterface::Continent
);
795 return doCompare(continent
, res
, [](const std::string
& a
, const std::string
& b
) {
796 return !strcasecmp(a
.c_str(), b
.c_str());
800 lua
.writeFunction("asnum", [&bestwho
](const combovar_t
& asns
) {
801 string res
=getGeo(bestwho
.toString(), GeoIPInterface::ASn
);
802 return doCompare(asns
, res
, [](const std::string
& a
, const std::string
& b
) {
803 return !strcasecmp(a
.c_str(), b
.c_str());
807 lua
.writeFunction("country", [&bestwho
](const combovar_t
& var
) {
808 string res
= getGeo(bestwho
.toString(), GeoIPInterface::Country2
);
809 return doCompare(var
, res
, [](const std::string
& a
, const std::string
& b
) {
810 return !strcasecmp(a
.c_str(), b
.c_str());
815 lua
.writeFunction("netmask", [bestwho
](const iplist_t
& ips
) {
816 for(const auto& i
:ips
) {
817 Netmask
nm(i
.second
);
818 if(nm
.match(bestwho
))
826 {'192.168.0.0/16', '10.0.0.0/8'},
827 {'192.168.20.20', '192.168.20.21'}
830 {'0.0.0.0/0'}, {'192.0.2.1'}
834 lua
.writeFunction("view", [bestwho
](const vector
<pair
<int, vector
<pair
<int, iplist_t
> > > >& in
) {
835 for(const auto& rule
: in
) {
836 const auto& netmasks
=rule
.second
[0].second
;
837 const auto& destinations
=rule
.second
[1].second
;
838 for(const auto& nmpair
: netmasks
) {
839 Netmask
nm(nmpair
.second
);
840 if(nm
.match(bestwho
)) {
841 return destinations
[random() % destinations
.size()].second
;
845 return std::string();
850 lua
.writeFunction("include", [&lua
,zone
,zoneid
](string record
) {
852 vector
<DNSZoneRecord
> drs
= lookup(DNSName(record
) +zone
, QType::LUA
, zoneid
);
853 for(const auto& dr
: drs
) {
854 auto lr
= getRR
<LUARecordContent
>(dr
.dr
);
855 lua
.executeCode(lr
->getCode());
858 catch(std::exception
& e
) {
859 g_log
<<Logger::Error
<<"Failed to load include record for LUArecord "<<(DNSName(record
)+zone
)<<": "<<e
.what()<<endl
;
865 if(!code
.empty() && code
[0]!=';')
866 actual
= "return " + code
;
868 actual
= code
.substr(1);
870 auto content
=lua
.executeCode
<boost::variant
<string
, vector
<pair
<int, string
> > > >(actual
);
872 vector
<string
> contents
;
873 if(auto str
= boost::get
<string
>(&content
))
874 contents
.push_back(*str
);
876 for(const auto& c
: boost::get
<vector
<pair
<int,string
>>>(content
))
877 contents
.push_back(c
.second
);
879 for(const auto& content
: contents
) {
880 if(qtype
==QType::TXT
)
881 ret
.push_back(std::shared_ptr
<DNSRecordContent
>(DNSRecordContent::mastermake(qtype
, 1, '"'+content
+'"' )));
883 ret
.push_back(std::shared_ptr
<DNSRecordContent
>(DNSRecordContent::mastermake(qtype
, 1, content
)));
885 } catch(std::exception
&e
) {
886 g_log
<<Logger::Error
<<"Lua record reported: "<<e
.what()<<endl
;