]>
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
;
118 bool IsUpOracle::isUp(const CheckDesc
& cd
)
120 std::lock_guard
<std::mutex
> l(d_mutex
);
121 auto iter
= d_statuses
.find(cd
);
122 if(iter
== d_statuses
.end()) {
123 d_statuses
[cd
]=Checker
{std::thread(&IsUpOracle::checkTCPThread
, this, cd
.rem
, cd
.opts
), false};
126 return iter
->second
.status
;
130 bool IsUpOracle::isUp(const ComboAddress
& remote
, const opts_t
& opts
)
132 CheckDesc cd
{remote
, "", opts
};
136 bool IsUpOracle::isUp(const ComboAddress
& remote
, const std::string
& url
, const opts_t
& opts
)
138 CheckDesc cd
{remote
, url
, opts
};
139 std::lock_guard
<std::mutex
> l(d_mutex
);
140 auto iter
= d_statuses
.find(cd
);
141 if(iter
== d_statuses
.end()) {
142 // g_log<<Logger::Warning<<"Launching HTTP(s) status checker for "<<remote.toStringWithPort()<<" and URL "<<url<<endl;
143 d_statuses
[cd
]=Checker
{std::thread(&IsUpOracle::checkURLThread
, this, remote
, url
, opts
), false};
147 return iter
->second
.status
;
150 void IsUpOracle::checkTCPThread(ComboAddress rem
, const opts_t
& opts
)
152 CheckDesc cd
{rem
, "", opts
};
154 for(bool first
=true;;first
=false) {
156 Socket
s(rem
.sin4
.sin_family
, SOCK_STREAM
);
159 if(opts
.count("source")) {
160 src
=ComboAddress(opts
.at("source"));
165 g_log
<<Logger::Warning
<<"Lua record monitoring declaring TCP/IP "<<rem
.toStringWithPort()<<" ";
166 if(opts
.count("source"))
167 g_log
<<"(source "<<src
.toString()<<") ";
172 catch(NetworkError
& ne
) {
173 if(isUp(rem
, opts
) || first
)
174 g_log
<<Logger::Warning
<<"Lua record monitoring declaring TCP/IP "<<rem
.toStringWithPort()<<" DOWN: "<<ne
.what()<<endl
;
182 void IsUpOracle::checkURLThread(ComboAddress rem
, std::string url
, const opts_t
& opts
)
184 setDown(rem
, url
, opts
);
185 for(bool first
=true;;first
=false) {
190 if(opts
.count("source")) {
191 ComboAddress
src(opts
.at("source"));
192 content
=mc
.getURL(url
, &rem
, &src
);
195 content
=mc
.getURL(url
, &rem
);
197 if(opts
.count("stringmatch") && content
.find(opts
.at("stringmatch")) == string::npos
) {
198 throw std::runtime_error(boost::str(boost::format("unable to match content with `%s`") % opts
.at("stringmatch")));
200 if(!upStatus(rem
,url
,opts
))
201 g_log
<<Logger::Warning
<<"LUA record monitoring declaring "<<rem
.toString()<<" UP for URL "<<url
<<"!"<<endl
;
202 setUp(rem
, url
,opts
);
204 catch(std::exception
& ne
) {
205 if(upStatus(rem
,url
,opts
) || first
)
206 g_log
<<Logger::Warning
<<"LUA record monitoring declaring "<<rem
.toString()<<" DOWN for URL "<<url
<<", error: "<<ne
.what()<<endl
;
207 setDown(rem
,url
,opts
);
216 template<typename T
, typename C
>
217 bool doCompare(const T
& var
, const std::string
& res
, const C
& cmp
)
219 if(auto country
= boost::get
<string
>(&var
))
220 return cmp(*country
, res
);
222 auto countries
=boost::get
<vector
<pair
<int,string
> > >(&var
);
223 for(const auto& country
: *countries
) {
224 if(cmp(country
.second
, res
))
232 std::string
getGeo(const std::string
& ip
, GeoIPInterface::GeoIPQueryAttribute qa
)
234 static bool initialized
;
235 extern std::function
<std::string(const std::string
& ip
, int)> g_getGeo
;
238 g_log
<<Logger::Error
<<"LUA Record attempted to use GeoIPBackend functionality, but backend not launched"<<endl
;
244 return g_getGeo(ip
, (int)qa
);
247 static ComboAddress
pickrandom(const vector
<ComboAddress
>& ips
)
250 throw std::invalid_argument("The IP list cannot be empty");
252 return ips
[random() % ips
.size()];
255 static ComboAddress
hashed(const ComboAddress
& who
, const vector
<ComboAddress
>& ips
)
258 throw std::invalid_argument("The IP list cannot be empty");
260 ComboAddress::addressOnlyHash aoh
;
261 return ips
[aoh(who
) % ips
.size()];
265 static ComboAddress
pickwrandom(const vector
<pair
<int,ComboAddress
> >& wips
)
268 throw std::invalid_argument("The IP list cannot be empty");
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 return ComboAddress();
287 vector
<pair
<int, ComboAddress
> > pick
;
288 for(auto& i
: wips
) {
290 pick
.push_back({sum
, i
.second
});
292 ComboAddress::addressOnlyHash aoh
;
293 int r
= aoh(bestwho
) % sum
;
294 auto p
= upper_bound(pick
.begin(), pick
.end(),r
, [](int r
, const decltype(pick
)::value_type
& a
) { return r
< a
.first
;});
298 static bool getLatLon(const std::string
& ip
, double& lat
, double& lon
)
300 string inp
= getGeo(ip
, GeoIPInterface::Location
);
303 lat
=atof(inp
.c_str());
304 auto pos
=inp
.find(' ');
305 if(pos
!= string::npos
)
306 lon
=atof(inp
.c_str() + pos
);
310 static bool getLatLon(const std::string
& ip
, string
& loc
)
312 int latdeg
, latmin
, londeg
, lonmin
;
313 double latsec
, lonsec
;
314 char lathem
='X', lonhem
='X';
317 if(!getLatLon(ip
, lat
, lon
))
338 >>> min = int((R - int(R)) * 60.0)
339 >>> sec = (((R - int(R)) * 60.0) - min) * 60.0
340 >>> print("{}º {}' {}\"".format(deg, min, sec))
345 latmin
= (lat
- latdeg
)*60.0;
346 latsec
= (((lat
- latdeg
)*60.0) - latmin
)*60.0;
349 lonmin
= (lon
- londeg
)*60.0;
350 lonsec
= (((lon
- londeg
)*60.0) - lonmin
)*60.0;
352 // 51 59 00.000 N 5 55 00.000 E 4.00m 1.00m 10000.00m 10.00m
354 boost::format
fmt("%d %d %d %c %d %d %d %c 0.00m 1.00m 10000.00m 10.00m");
356 loc
= (fmt
% latdeg
% latmin
% latsec
% lathem
% londeg
% lonmin
% lonsec
% lonhem
).str();
360 static ComboAddress
pickclosest(const ComboAddress
& bestwho
, const vector
<ComboAddress
>& wips
)
363 throw std::invalid_argument("The IP list cannot be empty");
365 map
<double,vector
<ComboAddress
> > ranked
;
366 double wlat
=0, wlon
=0;
367 getLatLon(bestwho
.toString(), wlat
, wlon
);
368 // cout<<"bestwho "<<wlat<<", "<<wlon<<endl;
370 for(const auto& c
: wips
) {
372 getLatLon(c
.toString(), lat
, lon
);
373 // cout<<c.toString()<<": "<<lat<<", "<<lon<<endl;
374 double latdiff
= wlat
-lat
;
375 double londiff
= wlon
-lon
;
377 londiff
= 360 - londiff
;
378 double dist2
=latdiff
*latdiff
+ londiff
*londiff
;
379 // cout<<" distance: "<<sqrt(dist2) * 40000.0/360<<" km"<<endl; // length of a degree
380 ranked
[dist2
].push_back(c
);
382 return ranked
.begin()->second
[random() % ranked
.begin()->second
.size()];
385 static std::vector
<DNSZoneRecord
> lookup(const DNSName
& name
, uint16_t qtype
, int zoneid
)
387 static UeberBackend ub
;
388 static std::mutex mut
;
389 std::lock_guard
<std::mutex
> lock(mut
);
390 ub
.lookup(QType(qtype
), name
, nullptr, zoneid
);
392 vector
<DNSZoneRecord
> ret
;
399 static std::string
getOptionValue(const boost::optional
<std::unordered_map
<string
, string
>>& options
, const std::string
&name
, const std::string
&defaultValue
)
401 string selector
=defaultValue
;
403 if(options
->count(name
))
404 selector
=options
->find(name
)->second
;
409 static vector
<ComboAddress
> useSelector(const std::string
&selector
, const ComboAddress
& bestwho
, const vector
<ComboAddress
>& candidates
)
411 vector
<ComboAddress
> ret
;
415 else if(selector
=="random")
416 ret
.emplace_back(pickrandom(candidates
));
417 else if(selector
=="pickclosest")
418 ret
.emplace_back(pickclosest(bestwho
, candidates
));
419 else if(selector
=="hashed")
420 ret
.emplace_back(hashed(bestwho
, candidates
));
422 g_log
<<Logger::Warning
<<"LUA Record called with unknown selector '"<<selector
<<"'"<<endl
;
423 ret
.emplace_back(pickrandom(candidates
));
429 static vector
<string
> convIpListToString(const vector
<ComboAddress
> &comboAddresses
)
433 for (const auto& c
: comboAddresses
) {
434 ret
.emplace_back(c
.toString());
440 static vector
<ComboAddress
> convIplist(const iplist_t
& src
)
442 vector
<ComboAddress
> ret
;
444 for(const auto& ip
: src
) {
445 ret
.emplace_back(ip
.second
);
451 static vector
<pair
<int, ComboAddress
> > convWIplist(std::unordered_map
<int, wiplist_t
> src
)
453 vector
<pair
<int,ComboAddress
> > ret
;
455 for(const auto& i
: src
) {
456 ret
.emplace_back(atoi(i
.second
.at(1).c_str()), ComboAddress(i
.second
.at(2)));
462 std::vector
<shared_ptr
<DNSRecordContent
>> luaSynth(const std::string
& code
, const DNSName
& query
, const DNSName
& zone
, int zoneid
, const DNSPacket
& dnsp
, uint16_t qtype
)
466 std::vector
<shared_ptr
<DNSRecordContent
>> ret
;
468 LuaContext
& lua
= *alua
.getLua();
469 lua
.writeVariable("qname", query
);
470 lua
.writeVariable("who", dnsp
.getRemote());
471 lua
.writeVariable("dh", (dnsheader
*)&dnsp
.d
);
472 lua
.writeVariable("dnssecOK", dnsp
.d_dnssecOk
);
473 lua
.writeVariable("tcp", dnsp
.d_tcp
);
474 lua
.writeVariable("ednsPKTSize", dnsp
.d_ednsRawPacketSizeLimit
);
475 ComboAddress bestwho
;
476 if(dnsp
.hasEDNSSubnet()) {
477 lua
.writeVariable("ecswho", dnsp
.getRealRemote());
478 bestwho
=dnsp
.getRealRemote().getNetwork();
481 bestwho
=dnsp
.getRemote();
484 lua
.writeVariable("bestwho", bestwho
);
486 lua
.writeFunction("latlon", [&bestwho
]() {
488 getLatLon(bestwho
.toString(), lat
, lon
);
489 return std::to_string(lat
)+" "+std::to_string(lon
);
492 lua
.writeFunction("latlonloc", [&bestwho
]() {
494 getLatLon(bestwho
.toString(), loc
);
499 lua
.writeFunction("closestMagic", [&bestwho
,&query
]() {
500 vector
<ComboAddress
> candidates
;
501 // Getting something like 192-0-2-1.192-0-2-2.198-51-100-1.example.org
502 for(auto l
: query
.getRawLabels()) {
503 boost::replace_all(l
, "-", ".");
505 candidates
.emplace_back(l
);
506 } catch (const PDNSException
& e
) {
507 // no need to continue as we most likely reached the end of the ip list
511 return pickclosest(bestwho
, candidates
).toString();
514 lua
.writeFunction("latlonMagic", [&query
](){
515 auto labels
= query
.getRawLabels();
517 return std::string("unknown");
519 getLatLon(labels
[3]+"."+labels
[2]+"."+labels
[1]+"."+labels
[0], lat
, lon
);
520 return std::to_string(lat
)+" "+std::to_string(lon
);
524 lua
.writeFunction("createReverse", [&bestwho
,&query
,&zone
](string suffix
, boost::optional
<std::unordered_map
<string
,string
>> e
){
526 auto labels
= query
.getRawLabels();
528 return std::string("unknown");
530 vector
<ComboAddress
> candidates
;
532 // exceptions are relative to zone
533 // so, query comes in for 4.3.2.1.in-addr.arpa, zone is called 2.1.in-addr.arpa
534 // e["1.2.3.4"]="bert.powerdns.com" - should match, easy enough to do
535 // the issue is with classless delegation..
537 ComboAddress
req(labels
[3]+"."+labels
[2]+"."+labels
[1]+"."+labels
[0], 0);
538 const auto& uom
= *e
;
539 for(const auto& c
: uom
)
540 if(ComboAddress(c
.first
, 0) == req
)
545 boost::format
fmt(suffix
);
546 fmt
.exceptions( boost::io::all_error_bits
^ ( boost::io::too_many_args_bit
| boost::io::too_few_args_bit
) );
547 fmt
% labels
[3] % labels
[2] % labels
[1] % labels
[0];
549 fmt
% (labels
[3]+"-"+labels
[2]+"-"+labels
[1]+"-"+labels
[0]);
551 boost::format
fmt2("%02x%02x%02x%02x");
552 for(int i
=3; i
>=0; --i
)
553 fmt2
% atoi(labels
[i
].c_str());
559 catch(std::exception
& e
) {
560 g_log
<<Logger::Error
<<"error: "<<e
.what()<<endl
;
562 return std::string("error");
565 lua
.writeFunction("createForward", [&zone
, &query
]() {
566 DNSName rel
=query
.makeRelative(zone
);
567 auto parts
= rel
.getRawLabels();
569 return parts
[0]+"."+parts
[1]+"."+parts
[2]+"."+parts
[3];
570 if(parts
.size()==1) {
571 // either hex string, or 12-13-14-15
572 // cout<<parts[0]<<endl;
573 unsigned int x1
, x2
, x3
, x4
;
574 if(sscanf(parts
[0].c_str()+2, "%02x%02x%02x%02x", &x1
, &x2
, &x3
, &x4
)==4) {
575 return std::to_string(x1
)+"."+std::to_string(x2
)+"."+std::to_string(x3
)+"."+std::to_string(x4
);
580 return std::string("0.0.0.0");
583 lua
.writeFunction("createForward6", [&query
,&zone
]() {
584 DNSName rel
=query
.makeRelative(zone
);
585 auto parts
= rel
.getRawLabels();
586 if(parts
.size()==8) {
588 for(int i
=0; i
<8; ++i
) {
593 ComboAddress
ca(tot
);
594 return ca
.toString();
596 else if(parts
.size()==1) {
597 boost::replace_all(parts
[0],"-",":");
598 ComboAddress
ca(parts
[0]);
599 return ca
.toString();
602 return std::string("::");
606 lua
.writeFunction("createReverse6", [&bestwho
,&query
,&zone
](string suffix
, boost::optional
<std::unordered_map
<string
,string
>> e
){
607 vector
<ComboAddress
> candidates
;
610 auto labels
= query
.getRawLabels();
612 return std::string("unknown");
613 boost::format
fmt(suffix
);
614 fmt
.exceptions( boost::io::all_error_bits
^ ( boost::io::too_many_args_bit
| boost::io::too_few_args_bit
) );
618 vector
<string
> quads
;
619 for(int i
=0; i
<8; ++i
) {
623 for(int j
=0; j
<4; ++j
) {
624 quad
.append(1, labels
[31-i
*4-j
][0]);
625 together
+= labels
[31-i
*4-j
][0];
627 quads
.push_back(quad
);
629 ComboAddress
ip6(together
,0);
633 for(const auto& addr
: addrs
) {
634 // this makes sure we catch all forms of the address
635 if(ComboAddress(addr
.first
,0)==ip6
)
640 string dashed
=ip6
.toString();
641 boost::replace_all(dashed
, ":", "-");
643 for(int i
=31; i
>=0; --i
)
647 for(const auto& quad
: quads
)
652 catch(std::exception
& e
) {
653 g_log
<<Logger::Error
<<"LUA Record xception: "<<e
.what()<<endl
;
655 catch(PDNSException
& e
) {
656 g_log
<<Logger::Error
<<"LUA Record exception: "<<e
.reason
<<endl
;
658 return std::string("unknown");
663 * Simplistic test to see if an IP address listens on a certain port
664 * Will return a single IP address from the set of available IP addresses. If
665 * no IP address is available, will return a random element of the set of
666 * addresses suppplied for testing.
668 * @example ifportup(443, { '1.2.3.4', '5.4.3.2' })"
670 lua
.writeFunction("ifportup", [&bestwho
](int port
, const vector
<pair
<int, string
> >& ips
, const boost::optional
<std::unordered_map
<string
,string
>> options
) {
671 vector
<ComboAddress
> candidates
, unavailables
;
673 vector
<ComboAddress
> conv
;
674 std::string selector
;
678 for(const auto& i
: ips
) {
679 ComboAddress
rem(i
.second
, port
);
680 if(g_up
.isUp(rem
, opts
)) {
681 candidates
.push_back(rem
);
684 unavailables
.push_back(rem
);
687 if(!candidates
.empty()) {
688 // use regular selector
689 selector
= getOptionValue(options
, "selector", "random");
691 // All units are down, apply backupSelector on all candidates
692 candidates
= std::move(unavailables
);
693 selector
= getOptionValue(options
, "backupSelector", "random");
696 vector
<ComboAddress
> res
= useSelector(selector
, bestwho
, candidates
);
697 return convIpListToString(res
);
700 lua
.writeFunction("ifurlup", [&bestwho
](const std::string
& url
,
701 const boost::variant
<iplist_t
, ipunitlist_t
>& ips
,
702 boost::optional
<opts_t
> options
) {
704 vector
<vector
<ComboAddress
> > candidates
;
708 if(auto simple
= boost::get
<iplist_t
>(&ips
)) {
709 vector
<ComboAddress
> unit
= convIplist(*simple
);
710 candidates
.push_back(unit
);
712 auto units
= boost::get
<ipunitlist_t
>(ips
);
713 for(const auto& u
: units
) {
714 vector
<ComboAddress
> unit
= convIplist(u
.second
);
715 candidates
.push_back(unit
);
719 for(const auto& unit
: candidates
) {
720 vector
<ComboAddress
> available
;
721 for(const auto& c
: unit
) {
722 if(g_up
.isUp(c
, url
, opts
)) {
723 available
.push_back(c
);
726 if(!available
.empty()) {
727 vector
<ComboAddress
> res
= useSelector(getOptionValue(options
, "selector", "random"), bestwho
, available
);
728 return convIpListToString(res
);
732 // All units down, apply backupSelector on all candidates
733 vector
<ComboAddress
> ret
{};
734 for(const auto& unit
: candidates
) {
735 ret
.insert(ret
.end(), unit
.begin(), unit
.end());
738 vector
<ComboAddress
> res
= useSelector(getOptionValue(options
, "backupSelector", "random"), bestwho
, ret
);
739 return convIpListToString(res
);
743 /* idea: we have policies on vectors of ComboAddresses, like
744 random, pickwrandom, pickwhashed, pickclosest. In C++ this is ComboAddress in,
745 ComboAddress out. In Lua, vector string in, string out */
748 * Returns a random IP address from the supplied list
749 * @example pickrandom({ '1.2.3.4', '5.4.3.2' })"
751 lua
.writeFunction("pickrandom", [](const iplist_t
& ips
) {
752 vector
<ComboAddress
> conv
= convIplist(ips
);
754 return pickrandom(conv
).toString();
759 * Returns a random IP address from the supplied list, as weighted by the
760 * various ``weight`` parameters
761 * @example pickwrandom({ {100, '1.2.3.4'}, {50, '5.4.3.2'}, {1, '192.168.1.0'} })
763 lua
.writeFunction("pickwrandom", [](std::unordered_map
<int, wiplist_t
> ips
) {
764 vector
<pair
<int,ComboAddress
> > conv
= convWIplist(ips
);
766 return pickwrandom(conv
).toString();
770 * Based on the hash of `bestwho`, returns an IP address from the list
771 * supplied, as weighted by the various `weight` parameters
772 * @example pickwhashed({ {15, '1.2.3.4'}, {50, '5.4.3.2'} })
774 lua
.writeFunction("pickwhashed", [&bestwho
](std::unordered_map
<int, wiplist_t
> ips
) {
775 vector
<pair
<int,ComboAddress
> > conv
;
778 conv
.emplace_back(atoi(i
.second
[1].c_str()), ComboAddress(i
.second
[2]));
780 return pickwhashed(bestwho
, conv
).toString();
784 lua
.writeFunction("pickclosest", [&bestwho
](const iplist_t
& ips
) {
785 vector
<ComboAddress
> conv
= convIplist(ips
);
787 return pickclosest(bestwho
, conv
).toString();
793 lua
.writeFunction("report", [&counter
](string event
, boost::optional
<string
> line
){
794 throw std::runtime_error("Script took too long");
796 if (g_luaRecordExecLimit
> 0) {
797 lua
.executeCode(boost::str(boost::format("debug.sethook(report, '', %d)") % g_luaRecordExecLimit
));
800 // TODO: make this better. Accept netmask/CA objects; provide names for the attr constants
801 lua
.writeFunction("geoiplookup", [](const string
&ip
, const GeoIPInterface::GeoIPQueryAttribute attr
) {
802 return getGeo(ip
, attr
);
805 typedef const boost::variant
<string
,vector
<pair
<int,string
> > > combovar_t
;
806 lua
.writeFunction("continent", [&bestwho
](const combovar_t
& continent
) {
807 string res
=getGeo(bestwho
.toString(), GeoIPInterface::Continent
);
808 return doCompare(continent
, res
, [](const std::string
& a
, const std::string
& b
) {
809 return !strcasecmp(a
.c_str(), b
.c_str());
813 lua
.writeFunction("asnum", [&bestwho
](const combovar_t
& asns
) {
814 string res
=getGeo(bestwho
.toString(), GeoIPInterface::ASn
);
815 return doCompare(asns
, res
, [](const std::string
& a
, const std::string
& b
) {
816 return !strcasecmp(a
.c_str(), b
.c_str());
820 lua
.writeFunction("country", [&bestwho
](const combovar_t
& var
) {
821 string res
= getGeo(bestwho
.toString(), GeoIPInterface::Country2
);
822 return doCompare(var
, res
, [](const std::string
& a
, const std::string
& b
) {
823 return !strcasecmp(a
.c_str(), b
.c_str());
828 lua
.writeFunction("netmask", [bestwho
](const iplist_t
& ips
) {
829 for(const auto& i
:ips
) {
830 Netmask
nm(i
.second
);
831 if(nm
.match(bestwho
))
839 {'192.168.0.0/16', '10.0.0.0/8'},
840 {'192.168.20.20', '192.168.20.21'}
843 {'0.0.0.0/0'}, {'192.0.2.1'}
847 lua
.writeFunction("view", [bestwho
](const vector
<pair
<int, vector
<pair
<int, iplist_t
> > > >& in
) {
848 for(const auto& rule
: in
) {
849 const auto& netmasks
=rule
.second
[0].second
;
850 const auto& destinations
=rule
.second
[1].second
;
851 for(const auto& nmpair
: netmasks
) {
852 Netmask
nm(nmpair
.second
);
853 if(nm
.match(bestwho
)) {
854 return destinations
[random() % destinations
.size()].second
;
858 return std::string();
863 lua
.writeFunction("include", [&lua
,zone
,zoneid
](string record
) {
865 vector
<DNSZoneRecord
> drs
= lookup(DNSName(record
) +zone
, QType::LUA
, zoneid
);
866 for(const auto& dr
: drs
) {
867 auto lr
= getRR
<LUARecordContent
>(dr
.dr
);
868 lua
.executeCode(lr
->getCode());
871 catch(std::exception
& e
) {
872 g_log
<<Logger::Error
<<"Failed to load include record for LUArecord "<<(DNSName(record
)+zone
)<<": "<<e
.what()<<endl
;
878 if(!code
.empty() && code
[0]!=';')
879 actual
= "return " + code
;
881 actual
= code
.substr(1);
883 auto content
=lua
.executeCode
<boost::variant
<string
, vector
<pair
<int, string
> > > >(actual
);
885 vector
<string
> contents
;
886 if(auto str
= boost::get
<string
>(&content
))
887 contents
.push_back(*str
);
889 for(const auto& c
: boost::get
<vector
<pair
<int,string
>>>(content
))
890 contents
.push_back(c
.second
);
892 for(const auto& content
: contents
) {
893 if(qtype
==QType::TXT
)
894 ret
.push_back(DNSRecordContent::mastermake(qtype
, QClass::IN
, '"'+content
+'"' ));
896 ret
.push_back(DNSRecordContent::mastermake(qtype
, QClass::IN
, content
));
898 } catch(std::exception
&e
) {
899 g_log
<<Logger::Error
<<"Lua record reported: "<<e
.what()<<endl
;