]>
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); |
e92fb64f CHB |
269 | d_lw->registerMember("deviceId", &DNSQuestion::deviceId); |
270 | d_lw->registerMember("deviceName", &DNSQuestion::deviceName); | |
a3e7b735 | 271 | d_lw->registerMember("followupFunction", &DNSQuestion::followupFunction); |
272 | d_lw->registerMember("followupPrefix", &DNSQuestion::followupPrefix); | |
273 | d_lw->registerMember("followupName", &DNSQuestion::followupName); | |
6b8b26c8 | 274 | d_lw->registerMember("data", &DNSQuestion::data); |
275 | d_lw->registerMember("udpQuery", &DNSQuestion::udpQuery); | |
276 | d_lw->registerMember("udpAnswer", &DNSQuestion::udpAnswer); | |
277 | d_lw->registerMember("udpQueryDest", &DNSQuestion::udpQueryDest); | |
278 | d_lw->registerMember("udpCallback", &DNSQuestion::udpCallback); | |
667f6c7c | 279 | d_lw->registerMember("appliedPolicy", &DNSQuestion::appliedPolicy); |
98c28a68 RG |
280 | d_lw->registerMember<DNSFilterEngine::Policy, std::string>("policyName", |
281 | [](const DNSFilterEngine::Policy& pol) -> std::string { | |
282 | if(pol.d_name) | |
283 | return *pol.d_name; | |
284 | return std::string(); | |
285 | }, | |
286 | [](DNSFilterEngine::Policy& pol, const std::string& name) { | |
287 | pol.d_name = std::make_shared<std::string>(name); | |
288 | }); | |
db486de5 PL |
289 | d_lw->registerMember("policyKind", &DNSFilterEngine::Policy::d_kind); |
290 | d_lw->registerMember("policyTTL", &DNSFilterEngine::Policy::d_ttl); | |
98c28a68 RG |
291 | d_lw->registerMember<DNSFilterEngine::Policy, std::string>("policyCustom", |
292 | [](const DNSFilterEngine::Policy& pol) -> std::string { | |
6da513b2 RG |
293 | std::string result; |
294 | if (pol.d_kind != DNSFilterEngine::PolicyKind::Custom) { | |
295 | return result; | |
296 | } | |
297 | ||
298 | for (const auto& dr : pol.d_custom) { | |
299 | if (!result.empty()) { | |
300 | result += "\n"; | |
301 | } | |
302 | result += dr->getZoneRepresentation(); | |
303 | } | |
304 | ||
305 | return result; | |
db486de5 | 306 | }, |
98c28a68 | 307 | [](DNSFilterEngine::Policy& pol, const std::string& content) { |
db486de5 | 308 | // Only CNAMES for now, when we ever add a d_custom_type, there will be pain |
6da513b2 RG |
309 | pol.d_custom.clear(); |
310 | pol.d_custom.push_back(DNSRecordContent::mastermake(QType::CNAME, QClass::IN, content)); | |
db486de5 PL |
311 | } |
312 | ); | |
621e4e59 | 313 | d_lw->registerFunction("getDH", &DNSQuestion::getDH); |
e8340d27 | 314 | d_lw->registerFunction("getEDNSOptions", &DNSQuestion::getEDNSOptions); |
315 | d_lw->registerFunction("getEDNSOption", &DNSQuestion::getEDNSOption); | |
5ecf1d7e | 316 | d_lw->registerFunction("getEDNSSubnet", &DNSQuestion::getEDNSSubnet); |
e2fb3504 PL |
317 | d_lw->registerFunction("getEDNSFlags", &DNSQuestion::getEDNSFlags); |
318 | d_lw->registerFunction("getEDNSFlag", &DNSQuestion::getEDNSFlag); | |
a3e7b735 | 319 | d_lw->registerMember("name", &DNSRecord::d_name); |
320 | d_lw->registerMember("type", &DNSRecord::d_type); | |
321 | d_lw->registerMember("ttl", &DNSRecord::d_ttl); | |
57d0c73b | 322 | d_lw->registerMember("place", &DNSRecord::d_place); |
00b8cadc | 323 | |
29e6303a RG |
324 | d_lw->registerMember("size", &EDNSOptionViewValue::size); |
325 | d_lw->registerFunction<std::string(EDNSOptionViewValue::*)()>("getContent", [](const EDNSOptionViewValue& value) { return std::string(value.content, value.size); }); | |
326 | d_lw->registerFunction<size_t(EDNSOptionView::*)()>("count", [](const EDNSOptionView& option) { return option.values.size(); }); | |
6158a3b3 PD |
327 | d_lw->registerFunction<std::vector<string>(EDNSOptionView::*)()>("getValues", [] (const EDNSOptionView& option) { |
328 | std::vector<string> values; | |
29e6303a | 329 | for (const auto& value : option.values) { |
6158a3b3 | 330 | values.push_back(std::string(value.content, value.size)); |
29e6303a RG |
331 | } |
332 | return values; | |
333 | }); | |
334 | ||
335 | /* pre 4.2 API compatibility, when we had only one value for a given EDNS option */ | |
336 | d_lw->registerMember<uint16_t(EDNSOptionView::*)>("size", [](const EDNSOptionView& option) -> uint16_t { | |
337 | uint16_t result = 0; | |
338 | ||
339 | if (!option.values.empty()) { | |
340 | result = option.values.at(0).size; | |
341 | } | |
342 | return result; | |
343 | }, | |
344 | [](EDNSOptionView& option, uint16_t newSize) { (void) newSize; }); | |
345 | d_lw->registerFunction<std::string(EDNSOptionView::*)()>("getContent", [](const EDNSOptionView& option) { | |
346 | if (option.values.empty()) { | |
347 | return std::string(); | |
348 | } | |
349 | return std::string(option.values.at(0).content, option.values.at(0).size); }); | |
00b8cadc | 350 | |
a3e7b735 | 351 | d_lw->registerFunction<string(DNSRecord::*)()>("getContent", [](const DNSRecord& dr) { return dr.d_content->getZoneRepresentation(); }); |
7d5f094a | 352 | d_lw->registerFunction<boost::optional<ComboAddress>(DNSRecord::*)()>("getCA", [](const DNSRecord& dr) { |
353 | boost::optional<ComboAddress> ret; | |
354 | ||
355 | if(auto rec = std::dynamic_pointer_cast<ARecordContent>(dr.d_content)) | |
356 | ret=rec->getCA(53); | |
dd079764 RG |
357 | else if(auto aaaarec = std::dynamic_pointer_cast<AAAARecordContent>(dr.d_content)) |
358 | ret=aaaarec->getCA(53); | |
7d5f094a | 359 | return ret; |
360 | }); | |
a3e7b735 | 361 | |
362 | ||
6177a176 | 363 | 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 | 364 | d_lw->registerFunction("addAnswer", &DNSQuestion::addAnswer); |
08dcccd6 | 365 | d_lw->registerFunction("addRecord", &DNSQuestion::addRecord); |
a3e7b735 | 366 | d_lw->registerFunction("getRecords", &DNSQuestion::getRecords); |
367 | d_lw->registerFunction("setRecords", &DNSQuestion::setRecords); | |
368 | ||
48096cf0 | 369 | d_lw->registerFunction<void(DNSQuestion::*)(const std::string&)>("addPolicyTag", [](DNSQuestion& dq, const std::string& tag) { if (dq.policyTags) { dq.policyTags->push_back(tag); } }); |
667f6c7c | 370 | 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 |
371 | if (dq.policyTags) { |
372 | dq.policyTags->clear(); | |
373 | for (const auto& tag : tags) { | |
374 | dq.policyTags->push_back(tag.second); | |
375 | } | |
667f6c7c RG |
376 | } |
377 | }); | |
378 | d_lw->registerFunction<std::vector<std::pair<int, std::string> >(DNSQuestion::*)()>("getPolicyTags", [](const DNSQuestion& dq) { | |
379 | std::vector<std::pair<int, std::string> > ret; | |
48096cf0 RG |
380 | if (dq.policyTags) { |
381 | int count = 1; | |
382 | for (const auto& tag : *dq.policyTags) { | |
383 | ret.push_back({count++, tag}); | |
384 | } | |
667f6c7c RG |
385 | } |
386 | return ret; | |
387 | }); | |
388 | ||
0a273054 RG |
389 | d_lw->registerFunction<void(DNSQuestion::*)(const std::string&)>("discardPolicy", [](DNSQuestion& dq, const std::string& policy) { |
390 | if (dq.discardedPolicies) { | |
391 | (*dq.discardedPolicies)[policy] = true; | |
392 | } | |
393 | }); | |
394 | ||
a3e7b735 | 395 | d_lw->writeFunction("newDS", []() { return SuffixMatchNode(); }); |
805f3e03 PL |
396 | d_lw->registerFunction<void(SuffixMatchNode::*)(boost::variant<string,DNSName, vector<pair<unsigned int,string> > >)>( |
397 | "add", | |
398 | [](SuffixMatchNode&smn, const boost::variant<string,DNSName,vector<pair<unsigned int,string> > >& in){ | |
399 | try { | |
400 | if(auto s = boost::get<string>(&in)) { | |
401 | smn.add(DNSName(*s)); | |
402 | } | |
403 | else if(auto v = boost::get<vector<pair<unsigned int, string> > >(&in)) { | |
dd079764 RG |
404 | for(const auto& entry : *v) |
405 | smn.add(DNSName(entry.second)); | |
805f3e03 PL |
406 | } |
407 | else { | |
408 | smn.add(boost::get<DNSName>(in)); | |
409 | } | |
410 | } | |
411 | catch(std::exception& e) { | |
e6a9dde5 | 412 | g_log <<Logger::Error<<e.what()<<endl; |
805f3e03 PL |
413 | } |
414 | } | |
415 | ); | |
416 | ||
a3e7b735 | 417 | d_lw->registerFunction("check",(bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check); |
c6b99fc1 | 418 | d_lw->registerFunction("toString",(string (SuffixMatchNode::*)() const) &SuffixMatchNode::toString); |
a3e7b735 | 419 | |
70c21c40 | 420 | d_pd.push_back({"policykinds", in_t { |
db486de5 PL |
421 | {"NoAction", (int)DNSFilterEngine::PolicyKind::NoAction}, |
422 | {"Drop", (int)DNSFilterEngine::PolicyKind::Drop }, | |
423 | {"NXDOMAIN", (int)DNSFilterEngine::PolicyKind::NXDOMAIN}, | |
424 | {"NODATA", (int)DNSFilterEngine::PolicyKind::NODATA }, | |
425 | {"Truncate", (int)DNSFilterEngine::PolicyKind::Truncate}, | |
426 | {"Custom", (int)DNSFilterEngine::PolicyKind::Custom } | |
427 | }}); | |
428 | ||
a3e7b735 | 429 | for(const auto& n : QType::names) |
70c21c40 | 430 | d_pd.push_back({n.first, n.second}); |
1921a4c2 | 431 | |
70c21c40 | 432 | d_pd.push_back({"validationstates", in_t{ |
1921a4c2 RG |
433 | {"Indeterminate", Indeterminate }, |
434 | {"Bogus", Bogus }, | |
435 | {"Insecure", Insecure }, | |
436 | {"Secure", Secure }, | |
437 | }}); | |
438 | ||
70c21c40 | 439 | d_pd.push_back({"now", &g_now}); |
9f89a5f1 | 440 | |
441 | d_lw->writeFunction("getMetric", [](const std::string& str) { | |
442 | return DynMetric{getDynMetric(str)}; | |
443 | }); | |
444 | ||
445 | d_lw->registerFunction("inc", &DynMetric::inc); | |
446 | d_lw->registerFunction("incBy", &DynMetric::incBy); | |
447 | d_lw->registerFunction("set", &DynMetric::set); | |
448 | d_lw->registerFunction("get", &DynMetric::get); | |
b4015453 | 449 | |
b0b37121 RG |
450 | d_lw->writeFunction("getStat", [](const std::string& str) { |
451 | uint64_t result = 0; | |
452 | optional<uint64_t> value = getStatByName(str); | |
453 | if (value) { | |
454 | result = *value; | |
455 | } | |
456 | return result; | |
457 | }); | |
458 | ||
b4015453 RG |
459 | d_lw->writeFunction("getRecursorThreadId", []() { |
460 | return getRecursorThreadId(); | |
461 | }); | |
462 | ||
d705aad9 RG |
463 | d_lw->writeFunction("sendCustomSNMPTrap", [](const std::string& str) { |
464 | if (g_snmpAgent) { | |
465 | g_snmpAgent->sendCustomTrap(str); | |
466 | } | |
467 | }); | |
c2d0a26f AT |
468 | |
469 | d_lw->writeFunction("getregisteredname", [](const DNSName &dname) { | |
470 | return getRegisteredName(dname); | |
471 | }); | |
70c21c40 | 472 | } |
a3e7b735 | 473 | |
70c21c40 | 474 | void RecursorLua4::postLoad() { |
0a273054 | 475 | d_prerpz = d_lw->readVariable<boost::optional<luacall_t>>("prerpz").get_value_or(0); |
a3e7b735 | 476 | d_preresolve = d_lw->readVariable<boost::optional<luacall_t>>("preresolve").get_value_or(0); |
477 | d_nodata = d_lw->readVariable<boost::optional<luacall_t>>("nodata").get_value_or(0); | |
478 | d_nxdomain = d_lw->readVariable<boost::optional<luacall_t>>("nxdomain").get_value_or(0); | |
479 | d_postresolve = d_lw->readVariable<boost::optional<luacall_t>>("postresolve").get_value_or(0); | |
f90c7544 | 480 | d_preoutquery = d_lw->readVariable<boost::optional<luacall_t>>("preoutquery").get_value_or(0); |
a2f87dd1 | 481 | d_maintenance = d_lw->readVariable<boost::optional<luamaintenance_t>>("maintenance").get_value_or(0); |
f90c7544 | 482 | |
483 | d_ipfilter = d_lw->readVariable<boost::optional<ipfilter_t>>("ipfilter").get_value_or(0); | |
81c0afd8 | 484 | d_gettag = d_lw->readVariable<boost::optional<gettag_t>>("gettag").get_value_or(0); |
70fb28d9 | 485 | d_gettag_ffi = d_lw->readVariable<boost::optional<gettag_ffi_t>>("gettag_ffi").get_value_or(0); |
808c5ef7 | 486 | } |
487 | ||
a2f87dd1 CHB |
488 | void RecursorLua4::maintenance() const |
489 | { | |
490 | if (d_maintenance) { | |
491 | d_maintenance(); | |
492 | } | |
493 | } | |
494 | ||
5899ee54 | 495 | bool RecursorLua4::prerpz(DNSQuestion& dq, int& ret) const |
0a273054 | 496 | { |
ba21fcfe | 497 | return genhook(d_prerpz, dq, ret); |
0a273054 RG |
498 | } |
499 | ||
5899ee54 | 500 | bool RecursorLua4::preresolve(DNSQuestion& dq, int& ret) const |
808c5ef7 | 501 | { |
ba21fcfe | 502 | return genhook(d_preresolve, dq, ret); |
a3e7b735 | 503 | } |
504 | ||
5899ee54 | 505 | bool RecursorLua4::nxdomain(DNSQuestion& dq, int& ret) const |
a3e7b735 | 506 | { |
ba21fcfe | 507 | return genhook(d_nxdomain, dq, ret); |
a3e7b735 | 508 | } |
509 | ||
5899ee54 | 510 | bool RecursorLua4::nodata(DNSQuestion& dq, int& ret) const |
a3e7b735 | 511 | { |
ba21fcfe | 512 | return genhook(d_nodata, dq, ret); |
a3e7b735 | 513 | } |
514 | ||
5899ee54 | 515 | bool RecursorLua4::postresolve(DNSQuestion& dq, int& ret) const |
a3e7b735 | 516 | { |
ba21fcfe | 517 | return genhook(d_postresolve, dq, ret); |
a3e7b735 | 518 | } |
519 | ||
5899ee54 | 520 | bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret) const |
a3e7b735 | 521 | { |
ba21fcfe RG |
522 | bool variableAnswer = false; |
523 | bool wantsRPZ = false; | |
f1c7929a RG |
524 | bool logQuery = false; |
525 | RecursorLua4::DNSQuestion dq(ns, requestor, query, qtype.getCode(), isTcp, variableAnswer, wantsRPZ, logQuery); | |
6e505c5e | 526 | dq.currentRecords = &res; |
ba21fcfe RG |
527 | |
528 | return genhook(d_preoutquery, dq, ret); | |
a3e7b735 | 529 | } |
530 | ||
5899ee54 | 531 | bool RecursorLua4::ipfilter(const ComboAddress& remote, const ComboAddress& local, const struct dnsheader& dh) const |
a3e7b735 | 532 | { |
533 | if(d_ipfilter) | |
f5062066 | 534 | return d_ipfilter(remote, local, dh); |
f90c7544 | 535 | return false; // don't block |
a3e7b735 | 536 | } |
537 | ||
0a6a45c8 | 538 | 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, std::string& deviceName) const |
81c0afd8 | 539 | { |
02b47f43 | 540 | if(d_gettag) { |
dff843b2 | 541 | auto ret = d_gettag(remote, ednssubnet, local, qname, qtype, ednsOptions, tcp); |
02b47f43 RG |
542 | |
543 | if (policyTags) { | |
544 | const auto& tags = std::get<1>(ret); | |
545 | if (tags) { | |
546 | for (const auto& tag : *tags) { | |
547 | policyTags->push_back(tag.second); | |
548 | } | |
549 | } | |
550 | } | |
5fd2577f | 551 | const auto dataret = std::get<2>(ret); |
05c74122 RG |
552 | if (dataret) { |
553 | data = *dataret; | |
554 | } | |
67e31ebe RG |
555 | const auto reqIdret = std::get<3>(ret); |
556 | if (reqIdret) { | |
557 | requestorId = *reqIdret; | |
558 | } | |
590388d2 NC |
559 | const auto deviceIdret = std::get<4>(ret); |
560 | if (deviceIdret) { | |
561 | deviceId = *deviceIdret; | |
562 | } | |
0a6a45c8 CHB |
563 | |
564 | const auto deviceNameret = std::get<5>(ret); | |
565 | if (deviceNameret) { | |
566 | deviceName = *deviceNameret; | |
567 | } | |
02b47f43 RG |
568 | return std::get<0>(ret); |
569 | } | |
81c0afd8 | 570 | return 0; |
571 | } | |
572 | ||
70fb28d9 RG |
573 | struct pdns_ffi_param |
574 | { | |
575 | public: | |
0a6a45c8 | 576 | 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_, std::string& deviceName_, 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_), deviceName(deviceName_), ttlCap(ttlCap_), variable(variable_), logQuery(logQuery_), qtype(qtype_), tcp(tcp_) |
70fb28d9 RG |
577 | { |
578 | } | |
579 | ||
580 | std::unique_ptr<std::string> qnameStr{nullptr}; | |
581 | std::unique_ptr<std::string> localStr{nullptr}; | |
582 | std::unique_ptr<std::string> remoteStr{nullptr}; | |
583 | std::unique_ptr<std::string> ednssubnetStr{nullptr}; | |
584 | std::vector<pdns_ednsoption_t> ednsOptionsVect; | |
585 | ||
586 | const DNSName& qname; | |
587 | const ComboAddress& local; | |
588 | const ComboAddress& remote; | |
589 | const Netmask& ednssubnet; | |
590 | std::vector<std::string>& policyTags; | |
29e6303a | 591 | const EDNSOptionViewMap& ednsOptions; |
70fb28d9 RG |
592 | std::string& requestorId; |
593 | std::string& deviceId; | |
0a6a45c8 | 594 | std::string& deviceName; |
70fb28d9 RG |
595 | uint32_t& ttlCap; |
596 | bool& variable; | |
f1c7929a | 597 | bool& logQuery; |
70fb28d9 RG |
598 | |
599 | unsigned int tag{0}; | |
600 | uint16_t qtype; | |
601 | bool tcp; | |
602 | }; | |
603 | ||
0a6a45c8 | 604 | 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, std::string& deviceName, uint32_t& ttlCap, bool& variable, bool& logQuery) const |
70fb28d9 RG |
605 | { |
606 | if (d_gettag_ffi) { | |
0a6a45c8 | 607 | pdns_ffi_param_t param(qname, qtype, local, remote, ednssubnet, *policyTags, ednsOptions, requestorId, deviceId, deviceName, ttlCap, variable, tcp, logQuery); |
70fb28d9 RG |
608 | |
609 | auto ret = d_gettag_ffi(¶m); | |
610 | if (ret) { | |
611 | data = *ret; | |
612 | } | |
613 | ||
614 | return param.tag; | |
615 | } | |
616 | return 0; | |
617 | } | |
618 | ||
5899ee54 | 619 | bool RecursorLua4::genhook(const luacall_t& func, DNSQuestion& dq, int& ret) const |
a3e7b735 | 620 | { |
621 | if(!func) | |
808c5ef7 | 622 | return false; |
a3e7b735 | 623 | |
6e505c5e RG |
624 | if (dq.currentRecords) { |
625 | dq.records = *dq.currentRecords; | |
ba21fcfe | 626 | } else { |
6e505c5e | 627 | dq.records.clear(); |
ba21fcfe RG |
628 | } |
629 | ||
6e505c5e RG |
630 | dq.followupFunction.clear(); |
631 | dq.followupPrefix.clear(); | |
632 | dq.followupName.clear(); | |
633 | dq.udpQuery.clear(); | |
634 | dq.udpAnswer.clear(); | |
635 | dq.udpCallback.clear(); | |
ba21fcfe | 636 | |
6e505c5e | 637 | dq.rcode = ret; |
1c567515 | 638 | bool handled=func(&dq); |
2205c52b | 639 | |
a3e7b735 | 640 | if(handled) { |
f9e5e573 | 641 | loop:; |
6e505c5e | 642 | ret=dq.rcode; |
d2f97f2a | 643 | |
6e505c5e RG |
644 | if(!dq.followupFunction.empty()) { |
645 | if(dq.followupFunction=="followCNAMERecords") { | |
646 | ret = followCNAMERecords(dq.records, QType(dq.qtype)); | |
a3e7b735 | 647 | } |
6e505c5e RG |
648 | else if(dq.followupFunction=="getFakeAAAARecords") { |
649 | ret=getFakeAAAARecords(dq.followupName, dq.followupPrefix, dq.records); | |
a3e7b735 | 650 | } |
6e505c5e RG |
651 | else if(dq.followupFunction=="getFakePTRRecords") { |
652 | ret=getFakePTRRecords(dq.followupName, dq.followupPrefix, dq.records); | |
a3e7b735 | 653 | } |
6e505c5e RG |
654 | else if(dq.followupFunction=="udpQueryResponse") { |
655 | dq.udpAnswer = GenUDPQueryResponse(dq.udpQueryDest, dq.udpQuery); | |
dd079764 RG |
656 | auto cbFunc = d_lw->readVariable<boost::optional<luacall_t>>(dq.udpCallback).get_value_or(0); |
657 | if(!cbFunc) { | |
e6a9dde5 | 658 | g_log<<Logger::Error<<"Attempted callback for Lua UDP Query/Response which could not be found"<<endl; |
6b8b26c8 | 659 | return false; |
d2f97f2a | 660 | } |
dd079764 | 661 | bool result=cbFunc(&dq); |
ba21fcfe | 662 | if(!result) { |
d2f97f2a | 663 | return false; |
664 | } | |
665 | goto loop; | |
f90c7544 | 666 | } |
a3e7b735 | 667 | } |
6e505c5e RG |
668 | if (dq.currentRecords) { |
669 | *dq.currentRecords = dq.records; | |
ba21fcfe | 670 | } |
a3e7b735 | 671 | } |
672 | ||
a3e7b735 | 673 | // see if they added followup work for us too |
674 | return handled; | |
675 | } | |
3dcc3fde | 676 | |
3dcc3fde | 677 | RecursorLua4::~RecursorLua4(){} |
70fb28d9 RG |
678 | |
679 | const char* pdns_ffi_param_get_qname(pdns_ffi_param_t* ref) | |
680 | { | |
681 | if (!ref->qnameStr) { | |
682 | ref->qnameStr = std::unique_ptr<std::string>(new std::string(ref->qname.toStringNoDot())); | |
683 | } | |
684 | ||
685 | return ref->qnameStr->c_str(); | |
686 | } | |
687 | ||
aedec560 RG |
688 | void pdns_ffi_param_get_qname_raw(pdns_ffi_param_t* ref, const char** qname, size_t* qnameSize) |
689 | { | |
690 | const auto& storage = ref->qname.getStorage(); | |
691 | *qname = storage.data(); | |
692 | *qnameSize = storage.size(); | |
693 | } | |
694 | ||
70fb28d9 RG |
695 | uint16_t pdns_ffi_param_get_qtype(const pdns_ffi_param_t* ref) |
696 | { | |
697 | return ref->qtype; | |
698 | } | |
699 | ||
700 | const char* pdns_ffi_param_get_remote(pdns_ffi_param_t* ref) | |
701 | { | |
702 | if (!ref->remoteStr) { | |
703 | ref->remoteStr = std::unique_ptr<std::string>(new std::string(ref->remote.toString())); | |
704 | } | |
705 | ||
706 | return ref->remoteStr->c_str(); | |
707 | } | |
708 | ||
aedec560 RG |
709 | static void pdns_ffi_comboaddress_to_raw(const ComboAddress& ca, const void** addr, size_t* addrSize) |
710 | { | |
711 | if (ca.isIPv4()) { | |
712 | *addr = &ca.sin4.sin_addr.s_addr; | |
713 | *addrSize = sizeof(ca.sin4.sin_addr.s_addr); | |
714 | } | |
715 | else { | |
716 | *addr = &ca.sin6.sin6_addr.s6_addr; | |
717 | *addrSize = sizeof(ca.sin6.sin6_addr.s6_addr); | |
718 | } | |
719 | } | |
720 | ||
721 | void pdns_ffi_param_get_remote_raw(pdns_ffi_param_t* ref, const void** addr, size_t* addrSize) | |
722 | { | |
723 | pdns_ffi_comboaddress_to_raw(ref->remote, addr, addrSize); | |
724 | } | |
725 | ||
70fb28d9 RG |
726 | uint16_t pdns_ffi_param_get_remote_port(const pdns_ffi_param_t* ref) |
727 | { | |
728 | return ref->remote.getPort(); | |
729 | } | |
730 | ||
731 | const char* pdns_ffi_param_get_local(pdns_ffi_param_t* ref) | |
732 | { | |
733 | if (!ref->localStr) { | |
734 | ref->localStr = std::unique_ptr<std::string>(new std::string(ref->local.toString())); | |
735 | } | |
736 | ||
737 | return ref->localStr->c_str(); | |
738 | } | |
739 | ||
aedec560 RG |
740 | void pdns_ffi_param_get_local_raw(pdns_ffi_param_t* ref, const void** addr, size_t* addrSize) |
741 | { | |
742 | pdns_ffi_comboaddress_to_raw(ref->local, addr, addrSize); | |
743 | } | |
744 | ||
70fb28d9 RG |
745 | uint16_t pdns_ffi_param_get_local_port(const pdns_ffi_param_t* ref) |
746 | { | |
747 | return ref->local.getPort(); | |
748 | } | |
749 | ||
750 | const char* pdns_ffi_param_get_edns_cs(pdns_ffi_param_t* ref) | |
751 | { | |
752 | if (ref->ednssubnet.empty()) { | |
753 | return nullptr; | |
754 | } | |
755 | ||
756 | if (!ref->ednssubnetStr) { | |
757 | ref->ednssubnetStr = std::unique_ptr<std::string>(new std::string(ref->ednssubnet.toStringNoMask())); | |
758 | } | |
759 | ||
760 | return ref->ednssubnetStr->c_str(); | |
761 | } | |
762 | ||
aedec560 RG |
763 | void pdns_ffi_param_get_edns_cs_raw(pdns_ffi_param_t* ref, const void** net, size_t* netSize) |
764 | { | |
765 | if (ref->ednssubnet.empty()) { | |
766 | *net = nullptr; | |
767 | *netSize = 0; | |
768 | return; | |
769 | } | |
770 | ||
771 | pdns_ffi_comboaddress_to_raw(ref->ednssubnet.getNetwork(), net, netSize); | |
772 | } | |
773 | ||
70fb28d9 RG |
774 | uint8_t pdns_ffi_param_get_edns_cs_source_mask(const pdns_ffi_param_t* ref) |
775 | { | |
776 | return ref->ednssubnet.getBits(); | |
777 | } | |
778 | ||
29e6303a | 779 | static void fill_edns_option(const EDNSOptionViewValue& value, pdns_ednsoption_t& option) |
70fb28d9 | 780 | { |
29e6303a | 781 | option.len = value.size; |
70fb28d9 RG |
782 | option.data = nullptr; |
783 | ||
29e6303a RG |
784 | if (value.size > 0) { |
785 | option.data = value.content; | |
70fb28d9 RG |
786 | } |
787 | } | |
788 | ||
789 | size_t pdns_ffi_param_get_edns_options(pdns_ffi_param_t* ref, const pdns_ednsoption_t** out) | |
790 | { | |
791 | if (ref->ednsOptions.empty()) { | |
792 | return 0; | |
793 | } | |
794 | ||
29e6303a RG |
795 | size_t totalCount = 0; |
796 | for (const auto& option : ref->ednsOptions) { | |
797 | totalCount += option.second.values.size(); | |
798 | } | |
799 | ||
800 | ref->ednsOptionsVect.resize(totalCount); | |
70fb28d9 RG |
801 | |
802 | size_t pos = 0; | |
29e6303a RG |
803 | for (const auto& option : ref->ednsOptions) { |
804 | for (const auto& entry : option.second.values) { | |
805 | fill_edns_option(entry, ref->ednsOptionsVect.at(pos)); | |
806 | ref->ednsOptionsVect.at(pos).optionCode = option.first; | |
807 | pos++; | |
808 | } | |
70fb28d9 RG |
809 | } |
810 | ||
811 | *out = ref->ednsOptionsVect.data(); | |
812 | ||
29e6303a | 813 | return totalCount; |
70fb28d9 RG |
814 | } |
815 | ||
816 | size_t pdns_ffi_param_get_edns_options_by_code(pdns_ffi_param_t* ref, uint16_t optionCode, const pdns_ednsoption_t** out) | |
817 | { | |
818 | const auto& it = ref->ednsOptions.find(optionCode); | |
29e6303a | 819 | if (it == ref->ednsOptions.cend() || it->second.values.empty()) { |
70fb28d9 RG |
820 | return 0; |
821 | } | |
822 | ||
29e6303a RG |
823 | ref->ednsOptionsVect.resize(it->second.values.size()); |
824 | ||
825 | size_t pos = 0; | |
826 | for (const auto& entry : it->second.values) { | |
827 | fill_edns_option(entry, ref->ednsOptionsVect.at(pos)); | |
828 | ref->ednsOptionsVect.at(pos).optionCode = optionCode; | |
829 | pos++; | |
830 | } | |
70fb28d9 RG |
831 | |
832 | *out = ref->ednsOptionsVect.data(); | |
833 | ||
29e6303a | 834 | return pos; |
70fb28d9 RG |
835 | } |
836 | ||
837 | void pdns_ffi_param_set_tag(pdns_ffi_param_t* ref, unsigned int tag) | |
838 | { | |
839 | ref->tag = tag; | |
840 | } | |
841 | ||
842 | void pdns_ffi_param_add_policytag(pdns_ffi_param_t *ref, const char* name) | |
843 | { | |
844 | ref->policyTags.push_back(std::string(name)); | |
845 | } | |
846 | ||
847 | void pdns_ffi_param_set_requestorid(pdns_ffi_param_t* ref, const char* name) | |
848 | { | |
849 | ref->requestorId = std::string(name); | |
850 | } | |
851 | ||
852 | void pdns_ffi_param_set_devicename(pdns_ffi_param_t* ref, const char* name) | |
853 | { | |
0a6a45c8 | 854 | ref->deviceName = std::string(name); |
70fb28d9 RG |
855 | } |
856 | ||
857 | void pdns_ffi_param_set_deviceid(pdns_ffi_param_t* ref, size_t len, const void* name) | |
858 | { | |
859 | ref->deviceId = std::string(reinterpret_cast<const char*>(name), len); | |
860 | } | |
861 | ||
862 | void pdns_ffi_param_set_variable(pdns_ffi_param_t* ref, bool variable) | |
863 | { | |
864 | ref->variable = variable; | |
865 | } | |
866 | ||
867 | void pdns_ffi_param_set_ttl_cap(pdns_ffi_param_t* ref, uint32_t ttl) | |
868 | { | |
869 | ref->ttlCap = ttl; | |
870 | } | |
f1c7929a RG |
871 | |
872 | void pdns_ffi_param_set_log_query(pdns_ffi_param_t* ref, bool logQuery) | |
873 | { | |
874 | ref->logQuery = logQuery; | |
875 | } |