]>
Commit | Line | Data |
---|---|---|
12471842 PL |
1 | /* |
2 | * This file is part of PowerDNS or dnsdist. | |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * In addition, for the avoidance of any doubt, permission is granted to | |
10 | * link this program with OpenSSL and to (re)distribute the binaries | |
11 | * produced as the result of such linking. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
808c5ef7 | 22 | #include "lua-recursor4.hh" |
23 | #include <fstream> | |
808c5ef7 | 24 | #include "logger.hh" |
a3e7b735 | 25 | #include "dnsparser.hh" |
26 | #include "syncres.hh" | |
dd39e2dc | 27 | #include "namespaces.hh" |
b40562da RG |
28 | #include "rec_channel.hh" |
29 | #include "ednsoptions.hh" | |
5ecf1d7e | 30 | #include "ednssubnet.hh" |
db486de5 | 31 | #include "filterpo.hh" |
d705aad9 | 32 | #include "rec-snmp.hh" |
08dcccd6 | 33 | #include <unordered_set> |
410d750c | 34 | |
9694e14f | 35 | RecursorLua4::RecursorLua4() { prepareContext(); } |
70c21c40 | 36 | |
a3e7b735 | 37 | static int followCNAMERecords(vector<DNSRecord>& ret, const QType& qtype) |
38 | { | |
39 | vector<DNSRecord> resolved; | |
40 | DNSName target; | |
41 | for(const DNSRecord& rr : ret) { | |
42 | if(rr.d_type == QType::CNAME) { | |
ba3c54cb RG |
43 | auto rec = getRR<CNAMERecordContent>(rr); |
44 | if(rec) { | |
45 | target=rec->getTarget(); | |
46 | break; | |
47 | } | |
a3e7b735 | 48 | } |
49 | } | |
50 | if(target.empty()) | |
51 | return 0; | |
52 | ||
53 | int rcode=directResolve(target, qtype, 1, resolved); // 1 == class | |
54 | ||
55 | for(const DNSRecord& rr : resolved) { | |
56 | ret.push_back(rr); | |
57 | } | |
58 | return rcode; | |
59 | ||
60 | } | |
61 | ||
62 | static int getFakeAAAARecords(const DNSName& qname, const std::string& prefix, vector<DNSRecord>& ret) | |
63 | { | |
64 | int rcode=directResolve(qname, QType(QType::A), 1, ret); | |
65 | ||
66 | ComboAddress prefixAddress(prefix); | |
67 | ||
9fc9be6a PL |
68 | // Remove double CNAME records |
69 | std::set<DNSName> seenCNAMEs; | |
70 | ret.erase(std::remove_if( | |
71 | ret.begin(), | |
72 | ret.end(), | |
73 | [&seenCNAMEs](DNSRecord& rr) { | |
74 | if (rr.d_type == QType::CNAME) { | |
75 | auto target = getRR<CNAMERecordContent>(rr); | |
76 | if (target == nullptr) { | |
77 | return false; | |
78 | } | |
79 | if (seenCNAMEs.count(target->getTarget()) > 0) { | |
80 | // We've had this CNAME before, remove it | |
81 | return true; | |
82 | } | |
83 | seenCNAMEs.insert(target->getTarget()); | |
84 | } | |
85 | return false; | |
86 | }), | |
87 | ret.end()); | |
88 | ||
8c0f5e09 | 89 | bool seenA = false; |
a3e7b735 | 90 | for(DNSRecord& rr : ret) |
91 | { | |
92 | if(rr.d_type == QType::A && rr.d_place==DNSResourceRecord::ANSWER) { | |
ba3c54cb RG |
93 | if(auto rec = getRR<ARecordContent>(rr)) { |
94 | ComboAddress ipv4(rec->getCA()); | |
95 | uint32_t tmp; | |
96 | memcpy((void*)&tmp, &ipv4.sin4.sin_addr.s_addr, 4); | |
97 | // tmp=htonl(tmp); | |
98 | memcpy(((char*)&prefixAddress.sin6.sin6_addr.s6_addr)+12, &tmp, 4); | |
99 | rr.d_content = std::make_shared<AAAARecordContent>(prefixAddress); | |
100 | rr.d_type = QType::AAAA; | |
101 | } | |
8c0f5e09 | 102 | seenA = true; |
a3e7b735 | 103 | } |
104 | } | |
8c0f5e09 PL |
105 | |
106 | if (seenA) { | |
107 | // We've seen an A in the ANSWER section, so there is no need to keep any | |
108 | // SOA in the AUTHORITY section as this is not a NODATA response. | |
109 | ret.erase(std::remove_if( | |
110 | ret.begin(), | |
111 | ret.end(), | |
112 | [](DNSRecord& rr) { | |
113 | return (rr.d_type == QType::SOA && rr.d_place==DNSResourceRecord::AUTHORITY); | |
114 | }), | |
115 | ret.end()); | |
116 | } | |
a3e7b735 | 117 | return rcode; |
118 | } | |
119 | ||
120 | static int getFakePTRRecords(const DNSName& qname, const std::string& prefix, vector<DNSRecord>& ret) | |
121 | { | |
122 | /* qname has a reverse ordered IPv6 address, need to extract the underlying IPv4 address from it | |
123 | and turn it into an IPv4 in-addr.arpa query */ | |
124 | ret.clear(); | |
125 | vector<string> parts = qname.getRawLabels(); | |
126 | ||
127 | if(parts.size() < 8) | |
128 | return -1; | |
129 | ||
130 | string newquery; | |
131 | for(int n = 0; n < 4; ++n) { | |
132 | newquery += | |
926ccaca | 133 | std::to_string(stoll(parts[n*2], 0, 16) + 16*stoll(parts[n*2+1], 0, 16)); |
a3e7b735 | 134 | newquery.append(1,'.'); |
135 | } | |
136 | newquery += "in-addr.arpa."; | |
137 | ||
138 | ||
139 | int rcode = directResolve(DNSName(newquery), QType(QType::PTR), 1, ret); | |
140 | for(DNSRecord& rr : ret) | |
141 | { | |
142 | if(rr.d_type == QType::PTR && rr.d_place==DNSResourceRecord::ANSWER) { | |
143 | rr.d_name = qname; | |
144 | } | |
145 | } | |
146 | return rcode; | |
147 | ||
148 | } | |
149 | ||
621e4e59 PL |
150 | boost::optional<dnsheader> RecursorLua4::DNSQuestion::getDH() const |
151 | { | |
152 | if (dh) | |
153 | return *dh; | |
154 | return boost::optional<dnsheader>(); | |
155 | } | |
156 | ||
e2fb3504 PL |
157 | vector<string> RecursorLua4::DNSQuestion::getEDNSFlags() const |
158 | { | |
159 | vector<string> ret; | |
160 | if (ednsFlags) { | |
161 | if (*ednsFlags & EDNSOpts::DNSSECOK) | |
162 | ret.push_back("DO"); | |
163 | } | |
164 | return ret; | |
165 | } | |
166 | ||
167 | bool RecursorLua4::DNSQuestion::getEDNSFlag(string flag) const | |
168 | { | |
169 | if (ednsFlags) { | |
170 | if (flag == "DO" && (*ednsFlags & EDNSOpts::DNSSECOK)) | |
171 | return true; | |
172 | } | |
173 | return false; | |
174 | } | |
175 | ||
ba21fcfe | 176 | vector<pair<uint16_t, string> > RecursorLua4::DNSQuestion::getEDNSOptions() const |
e8340d27 | 177 | { |
178 | if(ednsOptions) | |
179 | return *ednsOptions; | |
180 | else | |
181 | return vector<pair<uint16_t,string>>(); | |
182 | } | |
183 | ||
ba21fcfe | 184 | boost::optional<string> RecursorLua4::DNSQuestion::getEDNSOption(uint16_t code) const |
e8340d27 | 185 | { |
186 | if(ednsOptions) | |
187 | for(const auto& o : *ednsOptions) | |
188 | if(o.first==code) | |
189 | return o.second; | |
190 | ||
191 | return boost::optional<string>(); | |
192 | } | |
193 | ||
ba21fcfe | 194 | boost::optional<Netmask> RecursorLua4::DNSQuestion::getEDNSSubnet() const |
5ecf1d7e | 195 | { |
5ecf1d7e | 196 | if(ednsOptions) { |
197 | for(const auto& o : *ednsOptions) { | |
b40562da | 198 | if(o.first==EDNSOptionCode::ECS) { |
5ecf1d7e | 199 | EDNSSubnetOpts eso; |
200 | if(getEDNSSubnetOptsFromString(o.second, &eso)) | |
201 | return eso.source; | |
202 | else | |
203 | break; | |
204 | } | |
205 | } | |
206 | } | |
207 | return boost::optional<Netmask>(); | |
208 | } | |
209 | ||
e8340d27 | 210 | |
ba21fcfe | 211 | vector<pair<int, DNSRecord> > RecursorLua4::DNSQuestion::getRecords() const |
a3e7b735 | 212 | { |
213 | vector<pair<int, DNSRecord> > ret; | |
214 | int num=1; | |
215 | for(const auto& r : records) { | |
216 | ret.push_back({num++, r}); | |
217 | } | |
218 | return ret; | |
219 | } | |
220 | void RecursorLua4::DNSQuestion::setRecords(const vector<pair<int, DNSRecord> >& recs) | |
221 | { | |
222 | records.clear(); | |
223 | for(const auto& p : recs) { | |
224 | records.push_back(p.second); | |
a3e7b735 | 225 | } |
226 | } | |
227 | ||
aee72a7b | 228 | void RecursorLua4::DNSQuestion::addRecord(uint16_t type, const std::string& content, DNSResourceRecord::Place place, boost::optional<int> ttl, boost::optional<string> name) |
a3e7b735 | 229 | { |
230 | DNSRecord dr; | |
aee72a7b | 231 | dr.d_name=name ? DNSName(*name) : qname; |
a3e7b735 | 232 | dr.d_ttl=ttl.get_value_or(3600); |
233 | dr.d_type = type; | |
234 | dr.d_place = place; | |
6177a176 | 235 | dr.d_content = DNSRecordContent::mastermake(type, 1, content); |
a3e7b735 | 236 | records.push_back(dr); |
237 | } | |
238 | ||
aee72a7b | 239 | void RecursorLua4::DNSQuestion::addAnswer(uint16_t type, const std::string& content, boost::optional<int> ttl, boost::optional<string> name) |
a3e7b735 | 240 | { |
aee72a7b | 241 | addRecord(type, content, DNSResourceRecord::ANSWER, ttl, name); |
a3e7b735 | 242 | } |
9f89a5f1 | 243 | |
244 | struct DynMetric | |
245 | { | |
246 | std::atomic<unsigned long>* ptr; | |
247 | void inc() { (*ptr)++; } | |
248 | void incBy(unsigned int by) { (*ptr)+= by; } | |
249 | unsigned long get() { return *ptr; } | |
250 | void set(unsigned long val) { *ptr =val; } | |
251 | }; | |
252 | ||
70c21c40 | 253 | void RecursorLua4::postPrepareContext() |
808c5ef7 | 254 | { |
ba21fcfe RG |
255 | d_lw->registerMember<const DNSName (DNSQuestion::*)>("qname", [](const DNSQuestion& dq) -> const DNSName& { return dq.qname; }, [](DNSQuestion& dq, const DNSName& newName) { (void) newName; }); |
256 | d_lw->registerMember<uint16_t (DNSQuestion::*)>("qtype", [](const DNSQuestion& dq) -> uint16_t { return dq.qtype; }, [](DNSQuestion& dq, uint16_t newType) { (void) newType; }); | |
257 | d_lw->registerMember<bool (DNSQuestion::*)>("isTcp", [](const DNSQuestion& dq) -> bool { return dq.isTcp; }, [](DNSQuestion& dq, bool newTcp) { (void) newTcp; }); | |
258 | d_lw->registerMember<const ComboAddress (DNSQuestion::*)>("localaddr", [](const DNSQuestion& dq) -> const ComboAddress& { return dq.local; }, [](DNSQuestion& dq, const ComboAddress& newLocal) { (void) newLocal; }); | |
259 | d_lw->registerMember<const ComboAddress (DNSQuestion::*)>("remoteaddr", [](const DNSQuestion& dq) -> const ComboAddress& { return dq.remote; }, [](DNSQuestion& dq, const ComboAddress& newRemote) { (void) newRemote; }); | |
1921a4c2 | 260 | d_lw->registerMember<vState (DNSQuestion::*)>("validationState", [](const DNSQuestion& dq) -> vState { return dq.validationState; }, [](DNSQuestion& dq, vState newState) { (void) newState; }); |
ba21fcfe RG |
261 | |
262 | d_lw->registerMember<bool (DNSQuestion::*)>("variable", [](const DNSQuestion& dq) -> bool { return dq.variable; }, [](DNSQuestion& dq, bool newVariable) { dq.variable = newVariable; }); | |
263 | d_lw->registerMember<bool (DNSQuestion::*)>("wantsRPZ", [](const DNSQuestion& dq) -> bool { return dq.wantsRPZ; }, [](DNSQuestion& dq, bool newWantsRPZ) { dq.wantsRPZ = newWantsRPZ; }); | |
f1c7929a | 264 | d_lw->registerMember<bool (DNSQuestion::*)>("logResponse", [](const DNSQuestion& dq) -> bool { return dq.logResponse; }, [](DNSQuestion& dq, bool newLogResponse) { dq.logResponse = newLogResponse; }); |
ba21fcfe | 265 | |
a3e7b735 | 266 | d_lw->registerMember("rcode", &DNSQuestion::rcode); |
ba461517 | 267 | d_lw->registerMember("tag", &DNSQuestion::tag); |
67e31ebe | 268 | d_lw->registerMember("requestorId", &DNSQuestion::requestorId); |
a3e7b735 | 269 | d_lw->registerMember("followupFunction", &DNSQuestion::followupFunction); |
270 | d_lw->registerMember("followupPrefix", &DNSQuestion::followupPrefix); | |
271 | d_lw->registerMember("followupName", &DNSQuestion::followupName); | |
6b8b26c8 | 272 | d_lw->registerMember("data", &DNSQuestion::data); |
273 | d_lw->registerMember("udpQuery", &DNSQuestion::udpQuery); | |
274 | d_lw->registerMember("udpAnswer", &DNSQuestion::udpAnswer); | |
275 | d_lw->registerMember("udpQueryDest", &DNSQuestion::udpQueryDest); | |
276 | d_lw->registerMember("udpCallback", &DNSQuestion::udpCallback); | |
667f6c7c | 277 | d_lw->registerMember("appliedPolicy", &DNSQuestion::appliedPolicy); |
98c28a68 RG |
278 | d_lw->registerMember<DNSFilterEngine::Policy, std::string>("policyName", |
279 | [](const DNSFilterEngine::Policy& pol) -> std::string { | |
280 | if(pol.d_name) | |
281 | return *pol.d_name; | |
282 | return std::string(); | |
283 | }, | |
284 | [](DNSFilterEngine::Policy& pol, const std::string& name) { | |
285 | pol.d_name = std::make_shared<std::string>(name); | |
286 | }); | |
db486de5 PL |
287 | d_lw->registerMember("policyKind", &DNSFilterEngine::Policy::d_kind); |
288 | d_lw->registerMember("policyTTL", &DNSFilterEngine::Policy::d_ttl); | |
98c28a68 RG |
289 | d_lw->registerMember<DNSFilterEngine::Policy, std::string>("policyCustom", |
290 | [](const DNSFilterEngine::Policy& pol) -> std::string { | |
6da513b2 RG |
291 | std::string result; |
292 | if (pol.d_kind != DNSFilterEngine::PolicyKind::Custom) { | |
293 | return result; | |
294 | } | |
295 | ||
296 | for (const auto& dr : pol.d_custom) { | |
297 | if (!result.empty()) { | |
298 | result += "\n"; | |
299 | } | |
300 | result += dr->getZoneRepresentation(); | |
301 | } | |
302 | ||
303 | return result; | |
db486de5 | 304 | }, |
98c28a68 | 305 | [](DNSFilterEngine::Policy& pol, const std::string& content) { |
db486de5 | 306 | // Only CNAMES for now, when we ever add a d_custom_type, there will be pain |
6da513b2 RG |
307 | pol.d_custom.clear(); |
308 | pol.d_custom.push_back(DNSRecordContent::mastermake(QType::CNAME, QClass::IN, content)); | |
db486de5 PL |
309 | } |
310 | ); | |
621e4e59 | 311 | d_lw->registerFunction("getDH", &DNSQuestion::getDH); |
e8340d27 | 312 | d_lw->registerFunction("getEDNSOptions", &DNSQuestion::getEDNSOptions); |
313 | d_lw->registerFunction("getEDNSOption", &DNSQuestion::getEDNSOption); | |
5ecf1d7e | 314 | d_lw->registerFunction("getEDNSSubnet", &DNSQuestion::getEDNSSubnet); |
e2fb3504 PL |
315 | d_lw->registerFunction("getEDNSFlags", &DNSQuestion::getEDNSFlags); |
316 | d_lw->registerFunction("getEDNSFlag", &DNSQuestion::getEDNSFlag); | |
a3e7b735 | 317 | d_lw->registerMember("name", &DNSRecord::d_name); |
318 | d_lw->registerMember("type", &DNSRecord::d_type); | |
319 | d_lw->registerMember("ttl", &DNSRecord::d_ttl); | |
57d0c73b | 320 | d_lw->registerMember("place", &DNSRecord::d_place); |
00b8cadc | 321 | |
29e6303a RG |
322 | d_lw->registerMember("size", &EDNSOptionViewValue::size); |
323 | d_lw->registerFunction<std::string(EDNSOptionViewValue::*)()>("getContent", [](const EDNSOptionViewValue& value) { return std::string(value.content, value.size); }); | |
324 | d_lw->registerFunction<size_t(EDNSOptionView::*)()>("count", [](const EDNSOptionView& option) { return option.values.size(); }); | |
325 | d_lw->registerFunction<std::vector<std::pair<int, string>>(EDNSOptionView::*)()>("getValues", [] (const EDNSOptionView& option) { | |
326 | std::vector<std::pair<int, string> > values; | |
327 | for (const auto& value : option.values) { | |
328 | values.push_back(std::make_pair(values.size(), std::string(value.content, value.size))); | |
329 | } | |
330 | return values; | |
331 | }); | |
332 | ||
333 | /* pre 4.2 API compatibility, when we had only one value for a given EDNS option */ | |
334 | d_lw->registerMember<uint16_t(EDNSOptionView::*)>("size", [](const EDNSOptionView& option) -> uint16_t { | |
335 | uint16_t result = 0; | |
336 | ||
337 | if (!option.values.empty()) { | |
338 | result = option.values.at(0).size; | |
339 | } | |
340 | return result; | |
341 | }, | |
342 | [](EDNSOptionView& option, uint16_t newSize) { (void) newSize; }); | |
343 | d_lw->registerFunction<std::string(EDNSOptionView::*)()>("getContent", [](const EDNSOptionView& option) { | |
344 | if (option.values.empty()) { | |
345 | return std::string(); | |
346 | } | |
347 | return std::string(option.values.at(0).content, option.values.at(0).size); }); | |
00b8cadc | 348 | |
a3e7b735 | 349 | d_lw->registerFunction<string(DNSRecord::*)()>("getContent", [](const DNSRecord& dr) { return dr.d_content->getZoneRepresentation(); }); |
7d5f094a | 350 | d_lw->registerFunction<boost::optional<ComboAddress>(DNSRecord::*)()>("getCA", [](const DNSRecord& dr) { |
351 | boost::optional<ComboAddress> ret; | |
352 | ||
353 | if(auto rec = std::dynamic_pointer_cast<ARecordContent>(dr.d_content)) | |
354 | ret=rec->getCA(53); | |
dd079764 RG |
355 | else if(auto aaaarec = std::dynamic_pointer_cast<AAAARecordContent>(dr.d_content)) |
356 | ret=aaaarec->getCA(53); | |
7d5f094a | 357 | return ret; |
358 | }); | |
a3e7b735 | 359 | |
360 | ||
6177a176 | 361 | d_lw->registerFunction<void(DNSRecord::*)(const std::string&)>("changeContent", [](DNSRecord& dr, const std::string& newContent) { dr.d_content = DNSRecordContent::mastermake(dr.d_type, 1, newContent); }); |
a3e7b735 | 362 | d_lw->registerFunction("addAnswer", &DNSQuestion::addAnswer); |
08dcccd6 | 363 | d_lw->registerFunction("addRecord", &DNSQuestion::addRecord); |
a3e7b735 | 364 | d_lw->registerFunction("getRecords", &DNSQuestion::getRecords); |
365 | d_lw->registerFunction("setRecords", &DNSQuestion::setRecords); | |
366 | ||
48096cf0 | 367 | d_lw->registerFunction<void(DNSQuestion::*)(const std::string&)>("addPolicyTag", [](DNSQuestion& dq, const std::string& tag) { if (dq.policyTags) { dq.policyTags->push_back(tag); } }); |
667f6c7c | 368 | d_lw->registerFunction<void(DNSQuestion::*)(const std::vector<std::pair<int, std::string> >&)>("setPolicyTags", [](DNSQuestion& dq, const std::vector<std::pair<int, std::string> >& tags) { |
48096cf0 RG |
369 | if (dq.policyTags) { |
370 | dq.policyTags->clear(); | |
371 | for (const auto& tag : tags) { | |
372 | dq.policyTags->push_back(tag.second); | |
373 | } | |
667f6c7c RG |
374 | } |
375 | }); | |
376 | d_lw->registerFunction<std::vector<std::pair<int, std::string> >(DNSQuestion::*)()>("getPolicyTags", [](const DNSQuestion& dq) { | |
377 | std::vector<std::pair<int, std::string> > ret; | |
48096cf0 RG |
378 | if (dq.policyTags) { |
379 | int count = 1; | |
380 | for (const auto& tag : *dq.policyTags) { | |
381 | ret.push_back({count++, tag}); | |
382 | } | |
667f6c7c RG |
383 | } |
384 | return ret; | |
385 | }); | |
386 | ||
0a273054 RG |
387 | d_lw->registerFunction<void(DNSQuestion::*)(const std::string&)>("discardPolicy", [](DNSQuestion& dq, const std::string& policy) { |
388 | if (dq.discardedPolicies) { | |
389 | (*dq.discardedPolicies)[policy] = true; | |
390 | } | |
391 | }); | |
392 | ||
a3e7b735 | 393 | d_lw->writeFunction("newDS", []() { return SuffixMatchNode(); }); |
805f3e03 PL |
394 | d_lw->registerFunction<void(SuffixMatchNode::*)(boost::variant<string,DNSName, vector<pair<unsigned int,string> > >)>( |
395 | "add", | |
396 | [](SuffixMatchNode&smn, const boost::variant<string,DNSName,vector<pair<unsigned int,string> > >& in){ | |
397 | try { | |
398 | if(auto s = boost::get<string>(&in)) { | |
399 | smn.add(DNSName(*s)); | |
400 | } | |
401 | else if(auto v = boost::get<vector<pair<unsigned int, string> > >(&in)) { | |
dd079764 RG |
402 | for(const auto& entry : *v) |
403 | smn.add(DNSName(entry.second)); | |
805f3e03 PL |
404 | } |
405 | else { | |
406 | smn.add(boost::get<DNSName>(in)); | |
407 | } | |
408 | } | |
409 | catch(std::exception& e) { | |
e6a9dde5 | 410 | g_log <<Logger::Error<<e.what()<<endl; |
805f3e03 PL |
411 | } |
412 | } | |
413 | ); | |
414 | ||
a3e7b735 | 415 | d_lw->registerFunction("check",(bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check); |
c6b99fc1 | 416 | d_lw->registerFunction("toString",(string (SuffixMatchNode::*)() const) &SuffixMatchNode::toString); |
a3e7b735 | 417 | |
70c21c40 | 418 | d_pd.push_back({"policykinds", in_t { |
db486de5 PL |
419 | {"NoAction", (int)DNSFilterEngine::PolicyKind::NoAction}, |
420 | {"Drop", (int)DNSFilterEngine::PolicyKind::Drop }, | |
421 | {"NXDOMAIN", (int)DNSFilterEngine::PolicyKind::NXDOMAIN}, | |
422 | {"NODATA", (int)DNSFilterEngine::PolicyKind::NODATA }, | |
423 | {"Truncate", (int)DNSFilterEngine::PolicyKind::Truncate}, | |
424 | {"Custom", (int)DNSFilterEngine::PolicyKind::Custom } | |
425 | }}); | |
426 | ||
a3e7b735 | 427 | for(const auto& n : QType::names) |
70c21c40 | 428 | d_pd.push_back({n.first, n.second}); |
1921a4c2 | 429 | |
70c21c40 | 430 | d_pd.push_back({"validationstates", in_t{ |
1921a4c2 RG |
431 | {"Indeterminate", Indeterminate }, |
432 | {"Bogus", Bogus }, | |
433 | {"Insecure", Insecure }, | |
434 | {"Secure", Secure }, | |
435 | }}); | |
436 | ||
70c21c40 | 437 | d_pd.push_back({"now", &g_now}); |
9f89a5f1 | 438 | |
439 | d_lw->writeFunction("getMetric", [](const std::string& str) { | |
440 | return DynMetric{getDynMetric(str)}; | |
441 | }); | |
442 | ||
443 | d_lw->registerFunction("inc", &DynMetric::inc); | |
444 | d_lw->registerFunction("incBy", &DynMetric::incBy); | |
445 | d_lw->registerFunction("set", &DynMetric::set); | |
446 | d_lw->registerFunction("get", &DynMetric::get); | |
b4015453 | 447 | |
b0b37121 RG |
448 | d_lw->writeFunction("getStat", [](const std::string& str) { |
449 | uint64_t result = 0; | |
450 | optional<uint64_t> value = getStatByName(str); | |
451 | if (value) { | |
452 | result = *value; | |
453 | } | |
454 | return result; | |
455 | }); | |
456 | ||
b4015453 RG |
457 | d_lw->writeFunction("getRecursorThreadId", []() { |
458 | return getRecursorThreadId(); | |
459 | }); | |
460 | ||
d705aad9 RG |
461 | d_lw->writeFunction("sendCustomSNMPTrap", [](const std::string& str) { |
462 | if (g_snmpAgent) { | |
463 | g_snmpAgent->sendCustomTrap(str); | |
464 | } | |
465 | }); | |
c2d0a26f AT |
466 | |
467 | d_lw->writeFunction("getregisteredname", [](const DNSName &dname) { | |
468 | return getRegisteredName(dname); | |
469 | }); | |
70c21c40 | 470 | } |
a3e7b735 | 471 | |
70c21c40 | 472 | void RecursorLua4::postLoad() { |
0a273054 | 473 | d_prerpz = d_lw->readVariable<boost::optional<luacall_t>>("prerpz").get_value_or(0); |
a3e7b735 | 474 | d_preresolve = d_lw->readVariable<boost::optional<luacall_t>>("preresolve").get_value_or(0); |
475 | d_nodata = d_lw->readVariable<boost::optional<luacall_t>>("nodata").get_value_or(0); | |
476 | d_nxdomain = d_lw->readVariable<boost::optional<luacall_t>>("nxdomain").get_value_or(0); | |
477 | d_postresolve = d_lw->readVariable<boost::optional<luacall_t>>("postresolve").get_value_or(0); | |
f90c7544 | 478 | d_preoutquery = d_lw->readVariable<boost::optional<luacall_t>>("preoutquery").get_value_or(0); |
a2f87dd1 | 479 | d_maintenance = d_lw->readVariable<boost::optional<luamaintenance_t>>("maintenance").get_value_or(0); |
f90c7544 | 480 | |
481 | d_ipfilter = d_lw->readVariable<boost::optional<ipfilter_t>>("ipfilter").get_value_or(0); | |
81c0afd8 | 482 | d_gettag = d_lw->readVariable<boost::optional<gettag_t>>("gettag").get_value_or(0); |
70fb28d9 | 483 | d_gettag_ffi = d_lw->readVariable<boost::optional<gettag_ffi_t>>("gettag_ffi").get_value_or(0); |
808c5ef7 | 484 | } |
485 | ||
a2f87dd1 CHB |
486 | void RecursorLua4::maintenance() const |
487 | { | |
488 | if (d_maintenance) { | |
489 | d_maintenance(); | |
490 | } | |
491 | } | |
492 | ||
5899ee54 | 493 | bool RecursorLua4::prerpz(DNSQuestion& dq, int& ret) const |
0a273054 | 494 | { |
ba21fcfe | 495 | return genhook(d_prerpz, dq, ret); |
0a273054 RG |
496 | } |
497 | ||
5899ee54 | 498 | bool RecursorLua4::preresolve(DNSQuestion& dq, int& ret) const |
808c5ef7 | 499 | { |
ba21fcfe | 500 | return genhook(d_preresolve, dq, ret); |
a3e7b735 | 501 | } |
502 | ||
5899ee54 | 503 | bool RecursorLua4::nxdomain(DNSQuestion& dq, int& ret) const |
a3e7b735 | 504 | { |
ba21fcfe | 505 | return genhook(d_nxdomain, dq, ret); |
a3e7b735 | 506 | } |
507 | ||
5899ee54 | 508 | bool RecursorLua4::nodata(DNSQuestion& dq, int& ret) const |
a3e7b735 | 509 | { |
ba21fcfe | 510 | return genhook(d_nodata, dq, ret); |
a3e7b735 | 511 | } |
512 | ||
5899ee54 | 513 | bool RecursorLua4::postresolve(DNSQuestion& dq, int& ret) const |
a3e7b735 | 514 | { |
ba21fcfe | 515 | return genhook(d_postresolve, dq, ret); |
a3e7b735 | 516 | } |
517 | ||
5899ee54 | 518 | bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret) const |
a3e7b735 | 519 | { |
ba21fcfe RG |
520 | bool variableAnswer = false; |
521 | bool wantsRPZ = false; | |
f1c7929a RG |
522 | bool logQuery = false; |
523 | RecursorLua4::DNSQuestion dq(ns, requestor, query, qtype.getCode(), isTcp, variableAnswer, wantsRPZ, logQuery); | |
6e505c5e | 524 | dq.currentRecords = &res; |
ba21fcfe RG |
525 | |
526 | return genhook(d_preoutquery, dq, ret); | |
a3e7b735 | 527 | } |
528 | ||
5899ee54 | 529 | bool RecursorLua4::ipfilter(const ComboAddress& remote, const ComboAddress& local, const struct dnsheader& dh) const |
a3e7b735 | 530 | { |
531 | if(d_ipfilter) | |
f5062066 | 532 | return d_ipfilter(remote, local, dh); |
f90c7544 | 533 | return false; // don't block |
a3e7b735 | 534 | } |
535 | ||
29e6303a | 536 | unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId) const |
81c0afd8 | 537 | { |
02b47f43 | 538 | if(d_gettag) { |
dff843b2 | 539 | auto ret = d_gettag(remote, ednssubnet, local, qname, qtype, ednsOptions, tcp); |
02b47f43 RG |
540 | |
541 | if (policyTags) { | |
542 | const auto& tags = std::get<1>(ret); | |
543 | if (tags) { | |
544 | for (const auto& tag : *tags) { | |
545 | policyTags->push_back(tag.second); | |
546 | } | |
547 | } | |
548 | } | |
5fd2577f | 549 | const auto dataret = std::get<2>(ret); |
05c74122 RG |
550 | if (dataret) { |
551 | data = *dataret; | |
552 | } | |
67e31ebe RG |
553 | const auto reqIdret = std::get<3>(ret); |
554 | if (reqIdret) { | |
555 | requestorId = *reqIdret; | |
556 | } | |
590388d2 NC |
557 | const auto deviceIdret = std::get<4>(ret); |
558 | if (deviceIdret) { | |
559 | deviceId = *deviceIdret; | |
560 | } | |
02b47f43 RG |
561 | return std::get<0>(ret); |
562 | } | |
81c0afd8 | 563 | return 0; |
564 | } | |
565 | ||
70fb28d9 RG |
566 | struct pdns_ffi_param |
567 | { | |
568 | public: | |
f1c7929a | 569 | pdns_ffi_param(const DNSName& qname_, uint16_t qtype_, const ComboAddress& local_, const ComboAddress& remote_, const Netmask& ednssubnet_, std::vector<std::string>& policyTags_, const EDNSOptionViewMap& ednsOptions_, std::string& requestorId_, std::string& deviceId_, uint32_t& ttlCap_, bool& variable_, bool tcp_, bool& logQuery_): qname(qname_), local(local_), remote(remote_), ednssubnet(ednssubnet_), policyTags(policyTags_), ednsOptions(ednsOptions_), requestorId(requestorId_), deviceId(deviceId_), ttlCap(ttlCap_), variable(variable_), logQuery(logQuery_), qtype(qtype_), tcp(tcp_) |
70fb28d9 RG |
570 | { |
571 | } | |
572 | ||
573 | std::unique_ptr<std::string> qnameStr{nullptr}; | |
574 | std::unique_ptr<std::string> localStr{nullptr}; | |
575 | std::unique_ptr<std::string> remoteStr{nullptr}; | |
576 | std::unique_ptr<std::string> ednssubnetStr{nullptr}; | |
577 | std::vector<pdns_ednsoption_t> ednsOptionsVect; | |
578 | ||
579 | const DNSName& qname; | |
580 | const ComboAddress& local; | |
581 | const ComboAddress& remote; | |
582 | const Netmask& ednssubnet; | |
583 | std::vector<std::string>& policyTags; | |
29e6303a | 584 | const EDNSOptionViewMap& ednsOptions; |
70fb28d9 RG |
585 | std::string& requestorId; |
586 | std::string& deviceId; | |
587 | uint32_t& ttlCap; | |
588 | bool& variable; | |
f1c7929a | 589 | bool& logQuery; |
70fb28d9 RG |
590 | |
591 | unsigned int tag{0}; | |
592 | uint16_t qtype; | |
593 | bool tcp; | |
594 | }; | |
595 | ||
f1c7929a | 596 | unsigned int RecursorLua4::gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, uint32_t& ttlCap, bool& variable, bool& logQuery) const |
70fb28d9 RG |
597 | { |
598 | if (d_gettag_ffi) { | |
f1c7929a | 599 | pdns_ffi_param_t param(qname, qtype, local, remote, ednssubnet, *policyTags, ednsOptions, requestorId, deviceId, ttlCap, variable, tcp, logQuery); |
70fb28d9 RG |
600 | |
601 | auto ret = d_gettag_ffi(¶m); | |
602 | if (ret) { | |
603 | data = *ret; | |
604 | } | |
605 | ||
606 | return param.tag; | |
607 | } | |
608 | return 0; | |
609 | } | |
610 | ||
5899ee54 | 611 | bool RecursorLua4::genhook(const luacall_t& func, DNSQuestion& dq, int& ret) const |
a3e7b735 | 612 | { |
613 | if(!func) | |
808c5ef7 | 614 | return false; |
a3e7b735 | 615 | |
6e505c5e RG |
616 | if (dq.currentRecords) { |
617 | dq.records = *dq.currentRecords; | |
ba21fcfe | 618 | } else { |
6e505c5e | 619 | dq.records.clear(); |
ba21fcfe RG |
620 | } |
621 | ||
6e505c5e RG |
622 | dq.followupFunction.clear(); |
623 | dq.followupPrefix.clear(); | |
624 | dq.followupName.clear(); | |
625 | dq.udpQuery.clear(); | |
626 | dq.udpAnswer.clear(); | |
627 | dq.udpCallback.clear(); | |
ba21fcfe | 628 | |
6e505c5e | 629 | dq.rcode = ret; |
1c567515 | 630 | bool handled=func(&dq); |
2205c52b | 631 | |
a3e7b735 | 632 | if(handled) { |
f9e5e573 | 633 | loop:; |
6e505c5e | 634 | ret=dq.rcode; |
d2f97f2a | 635 | |
6e505c5e RG |
636 | if(!dq.followupFunction.empty()) { |
637 | if(dq.followupFunction=="followCNAMERecords") { | |
638 | ret = followCNAMERecords(dq.records, QType(dq.qtype)); | |
a3e7b735 | 639 | } |
6e505c5e RG |
640 | else if(dq.followupFunction=="getFakeAAAARecords") { |
641 | ret=getFakeAAAARecords(dq.followupName, dq.followupPrefix, dq.records); | |
a3e7b735 | 642 | } |
6e505c5e RG |
643 | else if(dq.followupFunction=="getFakePTRRecords") { |
644 | ret=getFakePTRRecords(dq.followupName, dq.followupPrefix, dq.records); | |
a3e7b735 | 645 | } |
6e505c5e RG |
646 | else if(dq.followupFunction=="udpQueryResponse") { |
647 | dq.udpAnswer = GenUDPQueryResponse(dq.udpQueryDest, dq.udpQuery); | |
dd079764 RG |
648 | auto cbFunc = d_lw->readVariable<boost::optional<luacall_t>>(dq.udpCallback).get_value_or(0); |
649 | if(!cbFunc) { | |
e6a9dde5 | 650 | g_log<<Logger::Error<<"Attempted callback for Lua UDP Query/Response which could not be found"<<endl; |
6b8b26c8 | 651 | return false; |
d2f97f2a | 652 | } |
dd079764 | 653 | bool result=cbFunc(&dq); |
ba21fcfe | 654 | if(!result) { |
d2f97f2a | 655 | return false; |
656 | } | |
657 | goto loop; | |
f90c7544 | 658 | } |
a3e7b735 | 659 | } |
6e505c5e RG |
660 | if (dq.currentRecords) { |
661 | *dq.currentRecords = dq.records; | |
ba21fcfe | 662 | } |
a3e7b735 | 663 | } |
664 | ||
a3e7b735 | 665 | // see if they added followup work for us too |
666 | return handled; | |
667 | } | |
3dcc3fde | 668 | |
3dcc3fde | 669 | RecursorLua4::~RecursorLua4(){} |
70fb28d9 RG |
670 | |
671 | const char* pdns_ffi_param_get_qname(pdns_ffi_param_t* ref) | |
672 | { | |
673 | if (!ref->qnameStr) { | |
674 | ref->qnameStr = std::unique_ptr<std::string>(new std::string(ref->qname.toStringNoDot())); | |
675 | } | |
676 | ||
677 | return ref->qnameStr->c_str(); | |
678 | } | |
679 | ||
aedec560 RG |
680 | void pdns_ffi_param_get_qname_raw(pdns_ffi_param_t* ref, const char** qname, size_t* qnameSize) |
681 | { | |
682 | const auto& storage = ref->qname.getStorage(); | |
683 | *qname = storage.data(); | |
684 | *qnameSize = storage.size(); | |
685 | } | |
686 | ||
70fb28d9 RG |
687 | uint16_t pdns_ffi_param_get_qtype(const pdns_ffi_param_t* ref) |
688 | { | |
689 | return ref->qtype; | |
690 | } | |
691 | ||
692 | const char* pdns_ffi_param_get_remote(pdns_ffi_param_t* ref) | |
693 | { | |
694 | if (!ref->remoteStr) { | |
695 | ref->remoteStr = std::unique_ptr<std::string>(new std::string(ref->remote.toString())); | |
696 | } | |
697 | ||
698 | return ref->remoteStr->c_str(); | |
699 | } | |
700 | ||
aedec560 RG |
701 | static void pdns_ffi_comboaddress_to_raw(const ComboAddress& ca, const void** addr, size_t* addrSize) |
702 | { | |
703 | if (ca.isIPv4()) { | |
704 | *addr = &ca.sin4.sin_addr.s_addr; | |
705 | *addrSize = sizeof(ca.sin4.sin_addr.s_addr); | |
706 | } | |
707 | else { | |
708 | *addr = &ca.sin6.sin6_addr.s6_addr; | |
709 | *addrSize = sizeof(ca.sin6.sin6_addr.s6_addr); | |
710 | } | |
711 | } | |
712 | ||
713 | void pdns_ffi_param_get_remote_raw(pdns_ffi_param_t* ref, const void** addr, size_t* addrSize) | |
714 | { | |
715 | pdns_ffi_comboaddress_to_raw(ref->remote, addr, addrSize); | |
716 | } | |
717 | ||
70fb28d9 RG |
718 | uint16_t pdns_ffi_param_get_remote_port(const pdns_ffi_param_t* ref) |
719 | { | |
720 | return ref->remote.getPort(); | |
721 | } | |
722 | ||
723 | const char* pdns_ffi_param_get_local(pdns_ffi_param_t* ref) | |
724 | { | |
725 | if (!ref->localStr) { | |
726 | ref->localStr = std::unique_ptr<std::string>(new std::string(ref->local.toString())); | |
727 | } | |
728 | ||
729 | return ref->localStr->c_str(); | |
730 | } | |
731 | ||
aedec560 RG |
732 | void pdns_ffi_param_get_local_raw(pdns_ffi_param_t* ref, const void** addr, size_t* addrSize) |
733 | { | |
734 | pdns_ffi_comboaddress_to_raw(ref->local, addr, addrSize); | |
735 | } | |
736 | ||
70fb28d9 RG |
737 | uint16_t pdns_ffi_param_get_local_port(const pdns_ffi_param_t* ref) |
738 | { | |
739 | return ref->local.getPort(); | |
740 | } | |
741 | ||
742 | const char* pdns_ffi_param_get_edns_cs(pdns_ffi_param_t* ref) | |
743 | { | |
744 | if (ref->ednssubnet.empty()) { | |
745 | return nullptr; | |
746 | } | |
747 | ||
748 | if (!ref->ednssubnetStr) { | |
749 | ref->ednssubnetStr = std::unique_ptr<std::string>(new std::string(ref->ednssubnet.toStringNoMask())); | |
750 | } | |
751 | ||
752 | return ref->ednssubnetStr->c_str(); | |
753 | } | |
754 | ||
aedec560 RG |
755 | void pdns_ffi_param_get_edns_cs_raw(pdns_ffi_param_t* ref, const void** net, size_t* netSize) |
756 | { | |
757 | if (ref->ednssubnet.empty()) { | |
758 | *net = nullptr; | |
759 | *netSize = 0; | |
760 | return; | |
761 | } | |
762 | ||
763 | pdns_ffi_comboaddress_to_raw(ref->ednssubnet.getNetwork(), net, netSize); | |
764 | } | |
765 | ||
70fb28d9 RG |
766 | uint8_t pdns_ffi_param_get_edns_cs_source_mask(const pdns_ffi_param_t* ref) |
767 | { | |
768 | return ref->ednssubnet.getBits(); | |
769 | } | |
770 | ||
29e6303a | 771 | static void fill_edns_option(const EDNSOptionViewValue& value, pdns_ednsoption_t& option) |
70fb28d9 | 772 | { |
29e6303a | 773 | option.len = value.size; |
70fb28d9 RG |
774 | option.data = nullptr; |
775 | ||
29e6303a RG |
776 | if (value.size > 0) { |
777 | option.data = value.content; | |
70fb28d9 RG |
778 | } |
779 | } | |
780 | ||
781 | size_t pdns_ffi_param_get_edns_options(pdns_ffi_param_t* ref, const pdns_ednsoption_t** out) | |
782 | { | |
783 | if (ref->ednsOptions.empty()) { | |
784 | return 0; | |
785 | } | |
786 | ||
29e6303a RG |
787 | size_t totalCount = 0; |
788 | for (const auto& option : ref->ednsOptions) { | |
789 | totalCount += option.second.values.size(); | |
790 | } | |
791 | ||
792 | ref->ednsOptionsVect.resize(totalCount); | |
70fb28d9 RG |
793 | |
794 | size_t pos = 0; | |
29e6303a RG |
795 | for (const auto& option : ref->ednsOptions) { |
796 | for (const auto& entry : option.second.values) { | |
797 | fill_edns_option(entry, ref->ednsOptionsVect.at(pos)); | |
798 | ref->ednsOptionsVect.at(pos).optionCode = option.first; | |
799 | pos++; | |
800 | } | |
70fb28d9 RG |
801 | } |
802 | ||
803 | *out = ref->ednsOptionsVect.data(); | |
804 | ||
29e6303a | 805 | return totalCount; |
70fb28d9 RG |
806 | } |
807 | ||
808 | size_t pdns_ffi_param_get_edns_options_by_code(pdns_ffi_param_t* ref, uint16_t optionCode, const pdns_ednsoption_t** out) | |
809 | { | |
810 | const auto& it = ref->ednsOptions.find(optionCode); | |
29e6303a | 811 | if (it == ref->ednsOptions.cend() || it->second.values.empty()) { |
70fb28d9 RG |
812 | return 0; |
813 | } | |
814 | ||
29e6303a RG |
815 | ref->ednsOptionsVect.resize(it->second.values.size()); |
816 | ||
817 | size_t pos = 0; | |
818 | for (const auto& entry : it->second.values) { | |
819 | fill_edns_option(entry, ref->ednsOptionsVect.at(pos)); | |
820 | ref->ednsOptionsVect.at(pos).optionCode = optionCode; | |
821 | pos++; | |
822 | } | |
70fb28d9 RG |
823 | |
824 | *out = ref->ednsOptionsVect.data(); | |
825 | ||
29e6303a | 826 | return pos; |
70fb28d9 RG |
827 | } |
828 | ||
829 | void pdns_ffi_param_set_tag(pdns_ffi_param_t* ref, unsigned int tag) | |
830 | { | |
831 | ref->tag = tag; | |
832 | } | |
833 | ||
834 | void pdns_ffi_param_add_policytag(pdns_ffi_param_t *ref, const char* name) | |
835 | { | |
836 | ref->policyTags.push_back(std::string(name)); | |
837 | } | |
838 | ||
839 | void pdns_ffi_param_set_requestorid(pdns_ffi_param_t* ref, const char* name) | |
840 | { | |
841 | ref->requestorId = std::string(name); | |
842 | } | |
843 | ||
844 | void pdns_ffi_param_set_devicename(pdns_ffi_param_t* ref, const char* name) | |
845 | { | |
846 | ref->deviceId = std::string(name); | |
847 | } | |
848 | ||
849 | void pdns_ffi_param_set_deviceid(pdns_ffi_param_t* ref, size_t len, const void* name) | |
850 | { | |
851 | ref->deviceId = std::string(reinterpret_cast<const char*>(name), len); | |
852 | } | |
853 | ||
854 | void pdns_ffi_param_set_variable(pdns_ffi_param_t* ref, bool variable) | |
855 | { | |
856 | ref->variable = variable; | |
857 | } | |
858 | ||
859 | void pdns_ffi_param_set_ttl_cap(pdns_ffi_param_t* ref, uint32_t ttl) | |
860 | { | |
861 | ref->ttlCap = ttl; | |
862 | } | |
f1c7929a RG |
863 | |
864 | void pdns_ffi_param_set_log_query(pdns_ffi_param_t* ref, bool logQuery) | |
865 | { | |
866 | ref->logQuery = logQuery; | |
867 | } |