]>
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
)
258 return ips
[random() % ips
.size()];
261 static ComboAddress
hashed(const ComboAddress
& who
, const vector
<ComboAddress
>& ips
)
263 ComboAddress::addressOnlyHash aoh
;
264 return ips
[aoh(who
) % ips
.size()];
268 static ComboAddress
pickwrandom(const vector
<pair
<int,ComboAddress
> >& wips
)
271 vector
<pair
<int, ComboAddress
> > pick
;
272 for(auto& i
: wips
) {
274 pick
.push_back({sum
, i
.second
});
276 int r
= random() % sum
;
277 auto p
= upper_bound(pick
.begin(), pick
.end(),r
, [](int r
, const decltype(pick
)::value_type
& a
) { return r
< a
.first
;});
281 static ComboAddress
pickwhashed(const ComboAddress
& bestwho
, vector
<pair
<int,ComboAddress
> >& wips
)
284 vector
<pair
<int, ComboAddress
> > pick
;
285 for(auto& i
: wips
) {
287 pick
.push_back({sum
, i
.second
});
289 ComboAddress::addressOnlyHash aoh
;
290 int r
= aoh(bestwho
) % sum
;
291 auto p
= upper_bound(pick
.begin(), pick
.end(),r
, [](int r
, const decltype(pick
)::value_type
& a
) { return r
< a
.first
;});
295 static bool getLatLon(const std::string
& ip
, double& lat
, double& lon
)
297 string inp
= getGeo(ip
, GeoIPInterface::Location
);
300 lat
=atof(inp
.c_str());
301 auto pos
=inp
.find(' ');
302 if(pos
!= string::npos
)
303 lon
=atof(inp
.c_str() + pos
);
307 static bool getLatLon(const std::string
& ip
, string
& loc
)
309 int latdeg
, latmin
, londeg
, lonmin
;
310 double latsec
, lonsec
;
311 char lathem
='X', lonhem
='X';
314 if(!getLatLon(ip
, lat
, lon
))
335 >>> min = int((R - int(R)) * 60.0)
336 >>> sec = (((R - int(R)) * 60.0) - min) * 60.0
337 >>> print("{}º {}' {}\"".format(deg, min, sec))
342 latmin
= (lat
- latdeg
)*60.0;
343 latsec
= (((lat
- latdeg
)*60.0) - latmin
)*60.0;
346 lonmin
= (lon
- londeg
)*60.0;
347 lonsec
= (((lon
- londeg
)*60.0) - lonmin
)*60.0;
349 // 51 59 00.000 N 5 55 00.000 E 4.00m 1.00m 10000.00m 10.00m
351 boost::format
fmt("%d %d %d %c %d %d %d %c 0.00m 1.00m 10000.00m 10.00m");
353 loc
= (fmt
% latdeg
% latmin
% latsec
% lathem
% londeg
% lonmin
% lonsec
% lonhem
).str();
357 static ComboAddress
pickclosest(const ComboAddress
& bestwho
, const vector
<ComboAddress
>& wips
)
359 map
<double,vector
<ComboAddress
> > ranked
;
360 double wlat
=0, wlon
=0;
361 getLatLon(bestwho
.toString(), wlat
, wlon
);
362 // cout<<"bestwho "<<wlat<<", "<<wlon<<endl;
364 for(const auto& c
: wips
) {
366 getLatLon(c
.toString(), lat
, lon
);
367 // cout<<c.toString()<<": "<<lat<<", "<<lon<<endl;
368 double latdiff
= wlat
-lat
;
369 double londiff
= wlon
-lon
;
371 londiff
= 360 - londiff
;
372 double dist2
=latdiff
*latdiff
+ londiff
*londiff
;
373 // cout<<" distance: "<<sqrt(dist2) * 40000.0/360<<" km"<<endl; // length of a degree
374 ranked
[dist2
].push_back(c
);
376 return ranked
.begin()->second
[random() % ranked
.begin()->second
.size()];
379 static std::vector
<DNSZoneRecord
> lookup(const DNSName
& name
, uint16_t qtype
, int zoneid
)
381 static UeberBackend ub
;
382 static std::mutex mut
;
383 std::lock_guard
<std::mutex
> lock(mut
);
384 ub
.lookup(QType(qtype
), name
, nullptr, zoneid
);
386 vector
<DNSZoneRecord
> ret
;
393 static ComboAddress
useSelector(const boost::optional
<std::unordered_map
<string
, string
>>& options
, const ComboAddress
& bestwho
, const vector
<ComboAddress
>& candidates
)
395 string selector
="random";
397 if(options
->count("selector"))
398 selector
=options
->find("selector")->second
;
401 if(selector
=="random")
402 return pickrandom(candidates
);
403 else if(selector
=="pickclosest")
404 return pickclosest(bestwho
, candidates
);
405 else if(selector
=="hashed")
406 return hashed(bestwho
, candidates
);
408 g_log
<<Logger::Warning
<<"LUA Record called with unknown selector '"<<selector
<<"'"<<endl
;
409 return pickrandom(candidates
);
412 static vector
<ComboAddress
> convIplist(const iplist_t
& src
)
414 vector
<ComboAddress
> ret
;
416 for(const auto& ip
: src
)
417 ret
.emplace_back(ip
.second
);
422 static vector
<pair
<int, ComboAddress
> > convWIplist(std::unordered_map
<int, wiplist_t
> src
)
424 vector
<pair
<int,ComboAddress
> > ret
;
426 for(const auto& i
: src
)
427 ret
.emplace_back(atoi(i
.second
.at(1).c_str()), ComboAddress(i
.second
.at(2)));
432 std::vector
<shared_ptr
<DNSRecordContent
>> luaSynth(const std::string
& code
, const DNSName
& query
, const DNSName
& zone
, int zoneid
, const DNSPacket
& dnsp
, uint16_t qtype
)
434 // cerr<<"Called for "<<query<<", in zone "<<zone<<" for type "<<qtype<<endl;
435 // cerr<<"Code: '"<<code<<"'"<<endl;
440 std::vector
<shared_ptr
<DNSRecordContent
>> ret
;
442 LuaContext
& lua
= *alua
.getLua();
443 lua
.writeVariable("qname", query
);
444 lua
.writeVariable("who", dnsp
.getRemote());
445 lua
.writeVariable("dh", (dnsheader
*)&dnsp
.d
);
446 lua
.writeVariable("dnssecOK", dnsp
.d_dnssecOk
);
447 lua
.writeVariable("tcp", dnsp
.d_tcp
);
448 lua
.writeVariable("ednsPKTSize", dnsp
.d_ednsRawPacketSizeLimit
);
449 ComboAddress bestwho
;
450 if(dnsp
.hasEDNSSubnet()) {
451 lua
.writeVariable("ecswho", dnsp
.getRealRemote());
452 bestwho
=dnsp
.getRealRemote().getNetwork();
455 bestwho
=dnsp
.getRemote();
458 lua
.writeVariable("bestwho", bestwho
);
460 lua
.writeFunction("latlon", [&bestwho
]() {
462 getLatLon(bestwho
.toString(), lat
, lon
);
463 return std::to_string(lat
)+" "+std::to_string(lon
);
466 lua
.writeFunction("latlonloc", [&bestwho
]() {
468 getLatLon(bestwho
.toString(), loc
);
473 lua
.writeFunction("closestMagic", [&bestwho
,&query
](){
474 vector
<ComboAddress
> candidates
;
475 for(auto l
: query
.getRawLabels()) {
476 boost::replace_all(l
, "-", ".");
478 candidates
.emplace_back(l
);
485 return pickclosest(bestwho
, candidates
).toString();
488 lua
.writeFunction("latlonMagic", [&query
](){
489 auto labels
= query
.getRawLabels();
491 return std::string("unknown");
493 getLatLon(labels
[3]+"."+labels
[2]+"."+labels
[1]+"."+labels
[0], lat
, lon
);
494 return std::to_string(lat
)+" "+std::to_string(lon
);
498 lua
.writeFunction("createReverse", [&bestwho
,&query
,&zone
](string suffix
, boost::optional
<std::unordered_map
<string
,string
>> e
){
500 auto labels
= query
.getRawLabels();
502 return std::string("unknown");
504 vector
<ComboAddress
> candidates
;
506 // exceptions are relative to zone
507 // so, query comes in for 4.3.2.1.in-addr.arpa, zone is called 2.1.in-addr.arpa
508 // e["1.2.3.4"]="bert.powerdns.com" - should match, easy enough to do
509 // the issue is with classless delegation..
511 ComboAddress
req(labels
[3]+"."+labels
[2]+"."+labels
[1]+"."+labels
[0], 0);
512 const auto& uom
= *e
;
513 for(const auto& c
: uom
)
514 if(ComboAddress(c
.first
, 0) == req
)
519 boost::format
fmt(suffix
);
520 fmt
.exceptions( boost::io::all_error_bits
^ ( boost::io::too_many_args_bit
| boost::io::too_few_args_bit
) );
521 fmt
% labels
[3] % labels
[2] % labels
[1] % labels
[0];
523 fmt
% (labels
[3]+"-"+labels
[2]+"-"+labels
[1]+"-"+labels
[0]);
525 boost::format
fmt2("%02x%02x%02x%02x");
526 for(int i
=3; i
>=0; --i
)
527 fmt2
% atoi(labels
[i
].c_str());
533 catch(std::exception
& e
) {
534 g_log
<<Logger::Error
<<"error: "<<e
.what()<<endl
;
536 return std::string("error");
539 lua
.writeFunction("createForward", [&zone
, &query
]() {
540 DNSName rel
=query
.makeRelative(zone
);
541 auto parts
= rel
.getRawLabels();
543 return parts
[0]+"."+parts
[1]+"."+parts
[2]+"."+parts
[3];
544 if(parts
.size()==1) {
545 // either hex string, or 12-13-14-15
546 // cout<<parts[0]<<endl;
548 if(sscanf(parts
[0].c_str()+2, "%02x%02x%02x%02x", &x1
, &x2
, &x3
, &x4
)==4) {
549 return std::to_string(x1
)+"."+std::to_string(x2
)+"."+std::to_string(x3
)+"."+std::to_string(x4
);
554 return std::string("0.0.0.0");
557 lua
.writeFunction("createForward6", [&query
,&zone
]() {
558 DNSName rel
=query
.makeRelative(zone
);
559 auto parts
= rel
.getRawLabels();
560 if(parts
.size()==8) {
562 for(int i
=0; i
<8; ++i
) {
567 ComboAddress
ca(tot
);
568 return ca
.toString();
570 else if(parts
.size()==1) {
571 boost::replace_all(parts
[0],"-",":");
572 ComboAddress
ca(parts
[0]);
573 return ca
.toString();
576 return std::string("::");
580 lua
.writeFunction("createReverse6", [&bestwho
,&query
,&zone
](string suffix
, boost::optional
<std::unordered_map
<string
,string
>> e
){
581 vector
<ComboAddress
> candidates
;
584 auto labels
= query
.getRawLabels();
586 return std::string("unknown");
587 boost::format
fmt(suffix
);
588 fmt
.exceptions( boost::io::all_error_bits
^ ( boost::io::too_many_args_bit
| boost::io::too_few_args_bit
) );
592 vector
<string
> quads
;
593 for(int i
=0; i
<8; ++i
) {
597 for(int j
=0; j
<4; ++j
) {
598 quad
.append(1, labels
[31-i
*4-j
][0]);
599 together
+= labels
[31-i
*4-j
][0];
601 quads
.push_back(quad
);
603 ComboAddress
ip6(together
,0);
607 for(const auto& addr
: addrs
) {
608 // this makes sure we catch all forms of the address
609 if(ComboAddress(addr
.first
,0)==ip6
)
614 string dashed
=ip6
.toString();
615 boost::replace_all(dashed
, ":", "-");
617 for(int i
=31; i
>=0; --i
)
621 for(const auto& quad
: quads
)
626 catch(std::exception
& e
) {
627 g_log
<<Logger::Error
<<"LUA Record xception: "<<e
.what()<<endl
;
629 catch(PDNSException
& e
) {
630 g_log
<<Logger::Error
<<"LUA Record exception: "<<e
.reason
<<endl
;
632 return std::string("unknown");
637 * Simplistic test to see if an IP address listens on a certain port
638 * Will return a single IP address from the set of available IP addresses. If
639 * no IP address is available, will return a random element of the set of
640 * addresses suppplied for testing.
642 * @example ifportup(443, { '1.2.3.4', '5.4.3.2' })"
644 lua
.writeFunction("ifportup", [&bestwho
](int port
, const vector
<pair
<int, string
> >& ips
, const boost::optional
<std::unordered_map
<string
,string
>> options
) {
645 vector
<ComboAddress
> candidates
, unavailables
;
647 vector
<ComboAddress
> conv
;
651 for(const auto& i
: ips
) {
652 ComboAddress
rem(i
.second
, port
);
653 if(g_up
.isUp(rem
, opts
)) {
654 candidates
.push_back(rem
);
657 unavailables
.push_back(rem
);
660 if(candidates
.empty()) {
661 // if no IP is available, use selector on the whole set
662 candidates
= std::move(unavailables
);
664 ComboAddress res
=useSelector(options
, bestwho
, candidates
);
666 return res
.toString();
669 lua
.writeFunction("ifurlup", [&bestwho
](const std::string
& url
,
670 const boost::variant
<iplist_t
, ipunitlist_t
>& ips
,
671 boost::optional
<opts_t
> options
) {
673 vector
<vector
<ComboAddress
> > candidates
;
677 if(auto simple
= boost::get
<iplist_t
>(&ips
)) {
678 vector
<ComboAddress
> unit
= convIplist(*simple
);
679 candidates
.push_back(unit
);
681 auto units
= boost::get
<ipunitlist_t
>(ips
);
682 for(const auto& u
: units
) {
683 vector
<ComboAddress
> unit
= convIplist(u
.second
);
684 candidates
.push_back(unit
);
688 for(const auto& unit
: candidates
) {
689 vector
<ComboAddress
> available
;
690 for(const auto& c
: unit
) {
691 if(g_up
.isUp(c
, url
, opts
)) {
692 available
.push_back(c
);
695 if(!available
.empty()) {
696 ComboAddress res
=useSelector(options
, bestwho
, available
);
698 return res
.toString();
702 // All units down, return a single, random record
703 vector
<ComboAddress
> ret
{};
704 for(const auto& unit
: candidates
) {
705 ret
.insert(ret
.end(), unit
.begin(), unit
.end());
708 return pickrandom(ret
).toString();
712 /* idea: we have policies on vectors of ComboAddresses, like
713 random, pickwrandom, pickwhashed, pickclosest. In C++ this is ComboAddress in,
714 ComboAddress out. In Lua, vector string in, string out */
717 * Returns a random IP address from the supplied list
718 * @example pickrandom({ '1.2.3.4', '5.4.3.2' })"
720 lua
.writeFunction("pickrandom", [](const iplist_t
& ips
) {
721 vector
<ComboAddress
> conv
= convIplist(ips
);
723 return pickrandom(conv
).toString();
728 * Returns a random IP address from the supplied list, as weighted by the
729 * various ``weight`` parameters
730 * @example pickwrandom({ {100, '1.2.3.4'}, {50, '5.4.3.2'}, {1, '192.168.1.0'} })
732 lua
.writeFunction("pickwrandom", [](std::unordered_map
<int, wiplist_t
> ips
) {
733 vector
<pair
<int,ComboAddress
> > conv
= convWIplist(ips
);
735 return pickwrandom(conv
).toString();
739 * Based on the hash of `bestwho`, returns an IP address from the list
740 * supplied, as weighted by the various `weight` parameters
741 * @example pickwhashed({ {15, '1.2.3.4'}, {50, '5.4.3.2'} })
743 lua
.writeFunction("pickwhashed", [&bestwho
](std::unordered_map
<int, wiplist_t
> ips
) {
744 vector
<pair
<int,ComboAddress
> > conv
;
747 conv
.emplace_back(atoi(i
.second
[1].c_str()), ComboAddress(i
.second
[2]));
749 return pickwhashed(bestwho
, conv
).toString();
753 lua
.writeFunction("pickclosest", [&bestwho
](const iplist_t
& ips
) {
754 vector
<ComboAddress
> conv
= convIplist(ips
);
756 return pickclosest(bestwho
, conv
).toString();
762 lua
.writeFunction("report", [&counter
](string event
, boost::optional
<string
> line
){
763 throw std::runtime_error("Script took too long");
765 if (g_luaRecordExecLimit
> 0) {
766 lua
.executeCode(boost::str(boost::format("debug.sethook(report, '', %d)") % g_luaRecordExecLimit
));
769 // TODO: make this better. Accept netmask/CA objects; provide names for the attr constants
770 lua
.writeFunction("geoiplookup", [](const string
&ip
, const GeoIPInterface::GeoIPQueryAttribute attr
) {
771 return getGeo(ip
, attr
);
774 typedef const boost::variant
<string
,vector
<pair
<int,string
> > > combovar_t
;
775 lua
.writeFunction("continent", [&bestwho
](const combovar_t
& continent
) {
776 string res
=getGeo(bestwho
.toString(), GeoIPInterface::Continent
);
777 return doCompare(continent
, res
, [](const std::string
& a
, const std::string
& b
) {
778 return !strcasecmp(a
.c_str(), b
.c_str());
782 lua
.writeFunction("asnum", [&bestwho
](const combovar_t
& asns
) {
783 string res
=getGeo(bestwho
.toString(), GeoIPInterface::ASn
);
784 return doCompare(asns
, res
, [](const std::string
& a
, const std::string
& b
) {
785 return !strcasecmp(a
.c_str(), b
.c_str());
789 lua
.writeFunction("country", [&bestwho
](const combovar_t
& var
) {
790 string res
= getGeo(bestwho
.toString(), GeoIPInterface::Country2
);
791 return doCompare(var
, res
, [](const std::string
& a
, const std::string
& b
) {
792 return !strcasecmp(a
.c_str(), b
.c_str());
797 lua
.writeFunction("netmask", [bestwho
](const iplist_t
& ips
) {
798 for(const auto& i
:ips
) {
799 Netmask
nm(i
.second
);
800 if(nm
.match(bestwho
))
808 {'192.168.0.0/16', '10.0.0.0/8'},
809 {'192.168.20.20', '192.168.20.21'}
812 {'0.0.0.0/0'}, {'192.0.2.1'}
816 lua
.writeFunction("view", [bestwho
](const vector
<pair
<int, vector
<pair
<int, iplist_t
> > > >& in
) {
817 for(const auto& rule
: in
) {
818 const auto& netmasks
=rule
.second
[0].second
;
819 const auto& destinations
=rule
.second
[1].second
;
820 for(const auto& nmpair
: netmasks
) {
821 Netmask
nm(nmpair
.second
);
822 if(nm
.match(bestwho
)) {
823 return destinations
[random() % destinations
.size()].second
;
827 return std::string();
832 lua
.writeFunction("include", [&lua
,zone
,zoneid
](string record
) {
834 vector
<DNSZoneRecord
> drs
= lookup(DNSName(record
) +zone
, QType::LUA
, zoneid
);
835 for(const auto& dr
: drs
) {
836 auto lr
= getRR
<LUARecordContent
>(dr
.dr
);
837 lua
.executeCode(lr
->getCode());
840 catch(std::exception
& e
) {
841 g_log
<<Logger::Error
<<"Failed to load include record for LUArecord "<<(DNSName(record
)+zone
)<<": "<<e
.what()<<endl
;
847 if(!code
.empty() && code
[0]!=';')
848 actual
= "return " + code
;
850 actual
= code
.substr(1);
852 auto content
=lua
.executeCode
<boost::variant
<string
, vector
<pair
<int, string
> > > >(actual
);
854 vector
<string
> contents
;
855 if(auto str
= boost::get
<string
>(&content
))
856 contents
.push_back(*str
);
858 for(const auto& c
: boost::get
<vector
<pair
<int,string
>>>(content
))
859 contents
.push_back(c
.second
);
861 for(const auto& content
: contents
) {
862 if(qtype
==QType::TXT
)
863 ret
.push_back(DNSRecordContent::mastermake(qtype
, QClass::IN
, '"'+content
+'"' ));
865 ret
.push_back(DNSRecordContent::mastermake(qtype
, QClass::IN
, content
));
867 } catch(std::exception
&e
) {
868 g_log
<<Logger::Error
<<"Lua record reported: "<<e
.what()<<endl
;