]>
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 | */ | |
fd9af6ec | 22 | #include <limits> |
870a0fe4 AT |
23 | #ifdef HAVE_CONFIG_H |
24 | #include "config.h" | |
25 | #endif | |
bf42c817 | 26 | #include "remotebackend.hh" |
fa8fd4d2 | 27 | |
ff05c7e1 | 28 | static const char* kBackendId = "[RemoteBackend]"; |
bf42c817 PD |
29 | |
30 | /** | |
31 | * Forwarder for value. This is just in case | |
32 | * we need to do some treatment to the value before | |
33 | * sending it downwards. | |
34 | */ | |
ff05c7e1 O |
35 | bool Connector::send(Json& value) |
36 | { | |
37 | return send_message(value) > 0; | |
bf42c817 PD |
38 | } |
39 | ||
fa2dd0e6 | 40 | /** |
bf42c817 | 41 | * Helper for handling receiving of data. |
fa2dd0e6 | 42 | * Basically what happens here is that we check |
bf42c817 | 43 | * that the receiving happened ok, and extract |
fa2dd0e6 | 44 | * result. Logging is performed here, too. |
bf42c817 | 45 | */ |
ff05c7e1 O |
46 | bool Connector::recv(Json& value) |
47 | { | |
2dc4f42e PL |
48 | if (recv_message(value) > 0) { |
49 | bool retval = true; | |
50 | if (value["result"] == Json()) { | |
51 | throw PDNSException("No 'result' field in response from remote process"); | |
ff05c7e1 | 52 | } |
645d66cf | 53 | if (value["result"].is_bool() && !boolFromJson(value, "result", false)) { |
2dc4f42e | 54 | retval = false; |
bf42c817 | 55 | } |
ff05c7e1 O |
56 | for (const auto& message : value["log"].array_items()) { |
57 | g_log << Logger::Info << "[remotebackend]: " << message.string_value() << std::endl; | |
2dc4f42e PL |
58 | } |
59 | return retval; | |
60 | } | |
61 | throw PDNSException("Unknown error while receiving data"); | |
62 | } | |
63 | ||
ff05c7e1 O |
64 | void RemoteBackend::makeErrorAndThrow(Json& value) |
65 | { | |
2dc4f42e | 66 | std::string msg = "Remote process indicated a failure"; |
ff05c7e1 | 67 | for (const auto& message : value["log"].array_items()) { |
2dc4f42e PL |
68 | msg += " '" + message.string_value() + "'"; |
69 | } | |
70 | throw PDNSException(msg); | |
bf42c817 PD |
71 | } |
72 | ||
fa2dd0e6 | 73 | /** |
bbd3f8b2 AT |
74 | * Standard ctor and dtor |
75 | */ | |
ff05c7e1 | 76 | RemoteBackend::RemoteBackend(const std::string& suffix) |
bf42c817 | 77 | { |
ff05c7e1 | 78 | setArgPrefix("remote" + suffix); |
4b64c6dc | 79 | |
ff05c7e1 O |
80 | this->d_connstr = getArg("connection-string"); |
81 | this->d_dnssec = mustDo("dnssec"); | |
fa2dd0e6 | 82 | |
ff05c7e1 | 83 | build(); |
bf42c817 PD |
84 | } |
85 | ||
645d66cf | 86 | RemoteBackend::~RemoteBackend() = default; |
bf42c817 | 87 | |
ff05c7e1 O |
88 | bool RemoteBackend::send(Json& value) |
89 | { | |
2dc4f42e PL |
90 | try { |
91 | if (!connector->send(value)) { | |
92 | // XXX does this work work even though we throw? | |
93 | this->connector.reset(); | |
94 | build(); | |
95 | throw DBException("Could not send a message to remote process"); | |
96 | } | |
ff05c7e1 O |
97 | } |
98 | catch (const PDNSException& ex) { | |
2dc4f42e PL |
99 | throw DBException("Exception caught when sending: " + ex.reason); |
100 | } | |
101 | return true; | |
4b64c6dc AT |
102 | } |
103 | ||
ff05c7e1 O |
104 | bool RemoteBackend::recv(Json& value) |
105 | { | |
2dc4f42e PL |
106 | try { |
107 | return connector->recv(value); | |
ff05c7e1 O |
108 | } |
109 | catch (const PDNSException& ex) { | |
2dc4f42e PL |
110 | this->connector.reset(); |
111 | build(); | |
112 | throw DBException("Exception caught when receiving: " + ex.reason); | |
ff05c7e1 O |
113 | } |
114 | catch (const std::exception& e) { | |
2dc4f42e PL |
115 | this->connector.reset(); |
116 | build(); | |
117 | throw DBException("Exception caught when receiving: " + std::string(e.what())); | |
118 | } | |
4b64c6dc AT |
119 | } |
120 | ||
fa2dd0e6 | 121 | /** |
bf42c817 PD |
122 | * Builds connector based on options |
123 | * Currently supports unix,pipe and http | |
124 | */ | |
ff05c7e1 O |
125 | int RemoteBackend::build() |
126 | { | |
127 | std::vector<std::string> parts; | |
128 | std::string type; | |
129 | std::string opts; | |
130 | std::map<std::string, std::string> options; | |
131 | ||
132 | // connstr is of format "type:options" | |
645d66cf FM |
133 | size_t pos = 0; |
134 | pos = d_connstr.find_first_of(':'); | |
135 | if (pos == std::string::npos) { | |
ff05c7e1 | 136 | throw PDNSException("Invalid connection string: malformed"); |
645d66cf | 137 | } |
ff05c7e1 O |
138 | |
139 | type = d_connstr.substr(0, pos); | |
140 | opts = d_connstr.substr(pos + 1); | |
141 | ||
142 | // tokenize the string on comma | |
143 | stringtok(parts, opts, ","); | |
144 | ||
145 | // find out some options and parse them while we're at it | |
146 | for (const auto& opt : parts) { | |
645d66cf FM |
147 | std::string key; |
148 | std::string val; | |
ff05c7e1 | 149 | // make sure there is something else than air in the option... |
645d66cf | 150 | if (opt.find_first_not_of(" ") == std::string::npos) { |
ff05c7e1 | 151 | continue; |
645d66cf | 152 | } |
ff05c7e1 O |
153 | |
154 | // split it on '='. if not found, we treat it as "yes" | |
155 | pos = opt.find_first_of("="); | |
156 | ||
157 | if (pos == std::string::npos) { | |
158 | key = opt; | |
159 | val = "yes"; | |
160 | } | |
161 | else { | |
162 | key = opt.substr(0, pos); | |
163 | val = opt.substr(pos + 1); | |
164 | } | |
165 | options[key] = val; | |
166 | } | |
167 | ||
168 | // connectors know what they are doing | |
169 | if (type == "unix") { | |
2bbc9eb0 | 170 | this->connector = std::make_unique<UnixsocketConnector>(options); |
ff05c7e1 O |
171 | } |
172 | else if (type == "http") { | |
2bbc9eb0 | 173 | this->connector = std::make_unique<HTTPConnector>(options); |
ff05c7e1 O |
174 | } |
175 | else if (type == "zeromq") { | |
a7db8aa6 | 176 | #ifdef REMOTEBACKEND_ZEROMQ |
2bbc9eb0 | 177 | this->connector = std::make_unique<ZeroMQConnector>(options); |
a7db8aa6 | 178 | #else |
ff05c7e1 | 179 | throw PDNSException("Invalid connection string: zeromq connector support not enabled. Recompile with --enable-remotebackend-zeromq"); |
72ffdde4 | 180 | #endif |
ff05c7e1 O |
181 | } |
182 | else if (type == "pipe") { | |
2bbc9eb0 | 183 | this->connector = std::make_unique<PipeConnector>(options); |
ff05c7e1 O |
184 | } |
185 | else { | |
186 | throw PDNSException("Invalid connection string: unknown connector"); | |
187 | } | |
bf42c817 | 188 | |
ff05c7e1 | 189 | return -1; |
bf42c817 PD |
190 | } |
191 | ||
fa2dd0e6 | 192 | /** |
bf42c817 | 193 | * The functions here are just remote json stubs that send and receive the method call |
fa2dd0e6 | 194 | * data is mainly left alone, some defaults are assumed. |
bf42c817 | 195 | */ |
ff05c7e1 O |
196 | void RemoteBackend::lookup(const QType& qtype, const DNSName& qdomain, int zoneId, DNSPacket* pkt_p) |
197 | { | |
645d66cf | 198 | if (d_index != -1) { |
ff05c7e1 | 199 | throw PDNSException("Attempt to lookup while one running"); |
645d66cf | 200 | } |
bf42c817 | 201 | |
ff05c7e1 O |
202 | string localIP = "0.0.0.0"; |
203 | string remoteIP = "0.0.0.0"; | |
204 | string realRemote = "0.0.0.0/0"; | |
95666839 | 205 | |
645d66cf | 206 | if (pkt_p != nullptr) { |
ff05c7e1 O |
207 | localIP = pkt_p->getLocal().toString(); |
208 | realRemote = pkt_p->getRealRemote().toString(); | |
7181a333 | 209 | remoteIP = pkt_p->getInnerRemote().toString(); |
ff05c7e1 O |
210 | } |
211 | ||
212 | Json query = Json::object{ | |
213 | {"method", "lookup"}, | |
d5fcd583 | 214 | {"parameters", Json::object{{"qtype", qtype.toString()}, {"qname", qdomain.toString()}, {"remote", remoteIP}, {"local", localIP}, {"real-remote", realRemote}, {"zone-id", zoneId}}}}; |
ff05c7e1 | 215 | |
645d66cf | 216 | if (!this->send(query) || !this->recv(d_result)) { |
ff05c7e1 O |
217 | return; |
218 | } | |
219 | ||
220 | // OK. we have result parameters in result. do not process empty result. | |
645d66cf | 221 | if (!d_result["result"].is_array() || d_result["result"].array_items().empty()) { |
ff05c7e1 | 222 | return; |
645d66cf | 223 | } |
ff05c7e1 O |
224 | |
225 | d_index = 0; | |
bf42c817 PD |
226 | } |
227 | ||
ff05c7e1 O |
228 | bool RemoteBackend::list(const DNSName& target, int domain_id, bool include_disabled) |
229 | { | |
645d66cf | 230 | if (d_index != -1) { |
ff05c7e1 | 231 | throw PDNSException("Attempt to lookup while one running"); |
645d66cf | 232 | } |
ff05c7e1 O |
233 | |
234 | Json query = Json::object{ | |
235 | {"method", "list"}, | |
236 | {"parameters", Json::object{{"zonename", target.toString()}, {"domain_id", domain_id}, {"include_disabled", include_disabled}}}}; | |
237 | ||
645d66cf | 238 | if (!this->send(query) || !this->recv(d_result)) { |
ff05c7e1 | 239 | return false; |
645d66cf FM |
240 | } |
241 | if (!d_result["result"].is_array() || d_result["result"].array_items().empty()) { | |
ff05c7e1 | 242 | return false; |
645d66cf | 243 | } |
ff05c7e1 O |
244 | |
245 | d_index = 0; | |
246 | return true; | |
bf42c817 PD |
247 | } |
248 | ||
ff05c7e1 O |
249 | bool RemoteBackend::get(DNSResourceRecord& rr) |
250 | { | |
645d66cf | 251 | if (d_index == -1) { |
ff05c7e1 | 252 | return false; |
645d66cf | 253 | } |
ff05c7e1 O |
254 | |
255 | rr.qtype = stringFromJson(d_result["result"][d_index], "qtype"); | |
256 | rr.qname = DNSName(stringFromJson(d_result["result"][d_index], "qname")); | |
257 | rr.qclass = QClass::IN; | |
258 | rr.content = stringFromJson(d_result["result"][d_index], "content"); | |
259 | rr.ttl = d_result["result"][d_index]["ttl"].int_value(); | |
260 | rr.domain_id = intFromJson(d_result["result"][d_index], "domain_id", -1); | |
645d66cf FM |
261 | if (d_dnssec) { |
262 | rr.auth = (intFromJson(d_result["result"][d_index], "auth", 1) != 0); | |
263 | } | |
264 | else { | |
265 | rr.auth = true; | |
266 | } | |
ff05c7e1 O |
267 | rr.scopeMask = d_result["result"][d_index]["scopeMask"].int_value(); |
268 | d_index++; | |
269 | ||
270 | // id index is out of bounds, we know the results end here. | |
271 | if (d_index == static_cast<int>(d_result["result"].array_items().size())) { | |
272 | d_result = Json(); | |
273 | d_index = -1; | |
274 | } | |
275 | return true; | |
c731caea AT |
276 | } |
277 | ||
ff05c7e1 O |
278 | bool RemoteBackend::getBeforeAndAfterNamesAbsolute(uint32_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after) |
279 | { | |
280 | // no point doing dnssec if it's not supported | |
645d66cf | 281 | if (!d_dnssec) { |
ff05c7e1 | 282 | return false; |
645d66cf | 283 | } |
ff05c7e1 O |
284 | |
285 | Json query = Json::object{ | |
286 | {"method", "getBeforeAndAfterNamesAbsolute"}, | |
287 | {"parameters", Json::object{{"id", Json(static_cast<double>(id))}, {"qname", qname.toString()}}}}; | |
288 | Json answer; | |
289 | ||
645d66cf | 290 | if (!this->send(query) || !this->recv(answer)) { |
ff05c7e1 | 291 | return false; |
645d66cf | 292 | } |
ff05c7e1 O |
293 | |
294 | unhashed = DNSName(stringFromJson(answer["result"], "unhashed")); | |
295 | before.clear(); | |
296 | after.clear(); | |
645d66cf | 297 | if (answer["result"]["before"] != Json()) { |
ff05c7e1 | 298 | before = DNSName(stringFromJson(answer["result"], "before")); |
645d66cf FM |
299 | } |
300 | if (answer["result"]["after"] != Json()) { | |
ff05c7e1 | 301 | after = DNSName(stringFromJson(answer["result"], "after")); |
645d66cf | 302 | } |
ff05c7e1 O |
303 | |
304 | return true; | |
bf42c817 PD |
305 | } |
306 | ||
ff05c7e1 O |
307 | bool RemoteBackend::getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string>>& meta) |
308 | { | |
309 | Json query = Json::object{ | |
310 | {"method", "getAllDomainMetadata"}, | |
311 | {"parameters", Json::object{{"name", name.toString()}}}}; | |
312 | ||
645d66cf | 313 | if (!this->send(query)) { |
ff05c7e1 | 314 | return false; |
645d66cf | 315 | } |
ff05c7e1 O |
316 | |
317 | meta.clear(); | |
318 | ||
319 | Json answer; | |
320 | // not mandatory to implement | |
645d66cf | 321 | if (!this->recv(answer)) { |
ff05c7e1 | 322 | return true; |
645d66cf | 323 | } |
ff05c7e1 O |
324 | |
325 | for (const auto& pair : answer["result"].object_items()) { | |
326 | if (pair.second.is_array()) { | |
645d66cf | 327 | for (const auto& val : pair.second.array_items()) { |
ff05c7e1 | 328 | meta[pair.first].push_back(asString(val)); |
645d66cf | 329 | } |
ff05c7e1 O |
330 | } |
331 | else { | |
332 | meta[pair.first].push_back(asString(pair.second)); | |
333 | } | |
334 | } | |
335 | ||
336 | return true; | |
bf42c817 PD |
337 | } |
338 | ||
ff05c7e1 O |
339 | bool RemoteBackend::getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta) |
340 | { | |
341 | Json query = Json::object{ | |
342 | {"method", "getDomainMetadata"}, | |
343 | {"parameters", Json::object{{"name", name.toString()}, {"kind", kind}}}}; | |
bf42c817 | 344 | |
645d66cf | 345 | if (!this->send(query)) { |
ff05c7e1 | 346 | return false; |
645d66cf | 347 | } |
bf42c817 | 348 | |
ff05c7e1 | 349 | meta.clear(); |
bf42c817 | 350 | |
ff05c7e1 O |
351 | Json answer; |
352 | // not mandatory to implement | |
645d66cf | 353 | if (!this->recv(answer)) { |
ff05c7e1 | 354 | return true; |
645d66cf | 355 | } |
bf42c817 | 356 | |
ff05c7e1 | 357 | if (answer["result"].is_array()) { |
645d66cf | 358 | for (const auto& row : answer["result"].array_items()) { |
ff05c7e1 | 359 | meta.push_back(row.string_value()); |
645d66cf | 360 | } |
ff05c7e1 O |
361 | } |
362 | else if (answer["result"].is_string()) { | |
363 | meta.push_back(answer["result"].string_value()); | |
364 | } | |
bf42c817 | 365 | |
ff05c7e1 O |
366 | return true; |
367 | } | |
368 | ||
369 | bool RemoteBackend::setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta) | |
370 | { | |
371 | Json query = Json::object{ | |
372 | {"method", "setDomainMetadata"}, | |
373 | {"parameters", Json::object{{"name", name.toString()}, {"kind", kind}, {"value", meta}}}}; | |
bf42c817 | 374 | |
ff05c7e1 | 375 | Json answer; |
645d66cf | 376 | if (!this->send(query) || !this->recv(answer)) { |
ff05c7e1 | 377 | return false; |
645d66cf | 378 | } |
ff05c7e1 O |
379 | |
380 | return boolFromJson(answer, "result", false); | |
bf42c817 PD |
381 | } |
382 | ||
ff05c7e1 O |
383 | bool RemoteBackend::getDomainKeys(const DNSName& name, std::vector<DNSBackend::KeyData>& keys) |
384 | { | |
385 | // no point doing dnssec if it's not supported | |
645d66cf | 386 | if (!d_dnssec) { |
ff05c7e1 | 387 | return false; |
645d66cf | 388 | } |
72ffdde4 | 389 | |
ff05c7e1 O |
390 | Json query = Json::object{ |
391 | {"method", "getDomainKeys"}, | |
392 | {"parameters", Json::object{{"name", name.toString()}}}}; | |
bf42c817 | 393 | |
ff05c7e1 | 394 | Json answer; |
645d66cf | 395 | if (!this->send(query) || !this->recv(answer)) { |
ff05c7e1 | 396 | return false; |
645d66cf | 397 | } |
bf42c817 | 398 | |
ff05c7e1 O |
399 | keys.clear(); |
400 | ||
401 | for (const auto& jsonKey : answer["result"].array_items()) { | |
402 | DNSBackend::KeyData key; | |
403 | key.id = intFromJson(jsonKey, "id"); | |
404 | key.flags = intFromJson(jsonKey, "flags"); | |
405 | key.active = asBool(jsonKey["active"]); | |
406 | key.published = boolFromJson(jsonKey, "published", true); | |
407 | key.content = stringFromJson(jsonKey, "content"); | |
408 | keys.push_back(key); | |
409 | } | |
410 | ||
411 | return true; | |
bf42c817 PD |
412 | } |
413 | ||
ff05c7e1 O |
414 | bool RemoteBackend::removeDomainKey(const DNSName& name, unsigned int id) |
415 | { | |
416 | // no point doing dnssec if it's not supported | |
645d66cf | 417 | if (!d_dnssec) { |
ff05c7e1 | 418 | return false; |
645d66cf | 419 | } |
ff05c7e1 O |
420 | |
421 | Json query = Json::object{ | |
422 | {"method", "removeDomainKey"}, | |
423 | {"parameters", Json::object{{"name", name.toString()}, {"id", static_cast<int>(id)}}}}; | |
424 | ||
425 | Json answer; | |
645d66cf | 426 | return this->send(query) && this->recv(answer); |
bf42c817 PD |
427 | } |
428 | ||
ff05c7e1 O |
429 | bool RemoteBackend::addDomainKey(const DNSName& name, const KeyData& key, int64_t& id) |
430 | { | |
431 | // no point doing dnssec if it's not supported | |
645d66cf | 432 | if (!d_dnssec) { |
ff05c7e1 | 433 | return false; |
645d66cf | 434 | } |
72ffdde4 | 435 | |
ff05c7e1 O |
436 | Json query = Json::object{ |
437 | {"method", "addDomainKey"}, | |
438 | {"parameters", Json::object{{"name", name.toString()}, {"key", Json::object{{"flags", static_cast<int>(key.flags)}, {"active", key.active}, {"published", key.published}, {"content", key.content}}}}}}; | |
bf42c817 | 439 | |
ff05c7e1 | 440 | Json answer; |
645d66cf | 441 | if (!this->send(query) || !this->recv(answer)) { |
ff05c7e1 | 442 | return false; |
645d66cf | 443 | } |
bf42c817 | 444 | |
ff05c7e1 O |
445 | id = answer["result"].int_value(); |
446 | return id >= 0; | |
bf42c817 PD |
447 | } |
448 | ||
ff05c7e1 O |
449 | bool RemoteBackend::activateDomainKey(const DNSName& name, unsigned int id) |
450 | { | |
451 | // no point doing dnssec if it's not supported | |
645d66cf | 452 | if (!d_dnssec) { |
ff05c7e1 | 453 | return false; |
645d66cf | 454 | } |
72ffdde4 | 455 | |
ff05c7e1 O |
456 | Json query = Json::object{ |
457 | {"method", "activateDomainKey"}, | |
458 | {"parameters", Json::object{{"name", name.toString()}, {"id", static_cast<int>(id)}}}}; | |
72ffdde4 | 459 | |
ff05c7e1 | 460 | Json answer; |
645d66cf | 461 | return this->send(query) && this->recv(answer); |
bf42c817 PD |
462 | } |
463 | ||
ff05c7e1 O |
464 | bool RemoteBackend::deactivateDomainKey(const DNSName& name, unsigned int id) |
465 | { | |
466 | // no point doing dnssec if it's not supported | |
645d66cf | 467 | if (!d_dnssec) { |
ff05c7e1 | 468 | return false; |
645d66cf | 469 | } |
33918299 | 470 | |
ff05c7e1 O |
471 | Json query = Json::object{ |
472 | {"method", "deactivateDomainKey"}, | |
473 | {"parameters", Json::object{{"name", name.toString()}, {"id", static_cast<int>(id)}}}}; | |
33918299 | 474 | |
ff05c7e1 | 475 | Json answer; |
645d66cf | 476 | return this->send(query) && this->recv(answer); |
33918299 RG |
477 | } |
478 | ||
ff05c7e1 O |
479 | bool RemoteBackend::publishDomainKey(const DNSName& name, unsigned int id) |
480 | { | |
481 | // no point doing dnssec if it's not supported | |
645d66cf | 482 | if (!d_dnssec) { |
ff05c7e1 | 483 | return false; |
645d66cf | 484 | } |
33918299 | 485 | |
ff05c7e1 O |
486 | Json query = Json::object{ |
487 | {"method", "publishDomainKey"}, | |
488 | {"parameters", Json::object{{"name", name.toString()}, {"id", static_cast<int>(id)}}}}; | |
33918299 | 489 | |
ff05c7e1 | 490 | Json answer; |
645d66cf | 491 | return this->send(query) && this->recv(answer); |
33918299 RG |
492 | } |
493 | ||
ff05c7e1 O |
494 | bool RemoteBackend::unpublishDomainKey(const DNSName& name, unsigned int id) |
495 | { | |
496 | // no point doing dnssec if it's not supported | |
645d66cf | 497 | if (!d_dnssec) { |
ff05c7e1 | 498 | return false; |
645d66cf | 499 | } |
ff05c7e1 O |
500 | |
501 | Json query = Json::object{ | |
502 | {"method", "unpublishDomainKey"}, | |
503 | {"parameters", Json::object{{"name", name.toString()}, {"id", static_cast<int>(id)}}}}; | |
504 | ||
505 | Json answer; | |
645d66cf | 506 | return this->send(query) && this->recv(answer); |
ff05c7e1 | 507 | } |
33918299 | 508 | |
ff05c7e1 O |
509 | bool RemoteBackend::doesDNSSEC() |
510 | { | |
511 | return d_dnssec; | |
f4644dfc PD |
512 | } |
513 | ||
40361bf2 | 514 | bool RemoteBackend::getTSIGKey(const DNSName& name, DNSName& algorithm, std::string& content) |
ff05c7e1 O |
515 | { |
516 | // no point doing dnssec if it's not supported | |
645d66cf | 517 | if (!d_dnssec) { |
ff05c7e1 | 518 | return false; |
645d66cf | 519 | } |
bf42c817 | 520 | |
ff05c7e1 O |
521 | Json query = Json::object{ |
522 | {"method", "getTSIGKey"}, | |
523 | {"parameters", Json::object{{"name", name.toString()}}}}; | |
fa2dd0e6 | 524 | |
ff05c7e1 | 525 | Json answer; |
645d66cf | 526 | if (!this->send(query) || !this->recv(answer)) { |
ff05c7e1 | 527 | return false; |
645d66cf | 528 | } |
bf42c817 | 529 | |
40361bf2 KM |
530 | algorithm = DNSName(stringFromJson(answer["result"], "algorithm")); |
531 | content = stringFromJson(answer["result"], "content"); | |
40d2dc07 | 532 | |
ff05c7e1 | 533 | return true; |
bf42c817 PD |
534 | } |
535 | ||
ff05c7e1 O |
536 | bool RemoteBackend::setTSIGKey(const DNSName& name, const DNSName& algorithm, const std::string& content) |
537 | { | |
538 | // no point doing dnssec if it's not supported | |
645d66cf | 539 | if (!d_dnssec) { |
ff05c7e1 | 540 | return false; |
645d66cf | 541 | } |
fa2dd0e6 | 542 | |
ff05c7e1 O |
543 | Json query = Json::object{ |
544 | {"method", "setTSIGKey"}, | |
545 | {"parameters", Json::object{{"name", name.toString()}, {"algorithm", algorithm.toString()}, {"content", content}}}}; | |
fa2dd0e6 | 546 | |
ff05c7e1 | 547 | Json answer; |
645d66cf | 548 | return connector->send(query) && connector->recv(answer); |
85f1a356 AT |
549 | } |
550 | ||
ff05c7e1 O |
551 | bool RemoteBackend::deleteTSIGKey(const DNSName& name) |
552 | { | |
553 | // no point doing dnssec if it's not supported | |
645d66cf | 554 | if (!d_dnssec) { |
ff05c7e1 | 555 | return false; |
645d66cf | 556 | } |
ff05c7e1 O |
557 | Json query = Json::object{ |
558 | {"method", "deleteTSIGKey"}, | |
559 | {"parameters", Json::object{{"name", name.toString()}}}}; | |
560 | ||
561 | Json answer; | |
645d66cf | 562 | return connector->send(query) && connector->recv(answer); |
85f1a356 AT |
563 | } |
564 | ||
ff05c7e1 O |
565 | bool RemoteBackend::getTSIGKeys(std::vector<struct TSIGKey>& keys) |
566 | { | |
567 | // no point doing dnssec if it's not supported | |
645d66cf | 568 | if (!d_dnssec) { |
ff05c7e1 | 569 | return false; |
645d66cf | 570 | } |
ff05c7e1 O |
571 | Json query = Json::object{ |
572 | {"method", "getTSIGKeys"}, | |
573 | {"parameters", Json::object{}}}; | |
574 | ||
575 | Json answer; | |
645d66cf | 576 | if (!connector->send(query) || !connector->recv(answer)) { |
ff05c7e1 | 577 | return false; |
645d66cf | 578 | } |
ff05c7e1 O |
579 | |
580 | for (const auto& jsonKey : answer["result"].array_items()) { | |
581 | struct TSIGKey key; | |
582 | key.name = DNSName(stringFromJson(jsonKey, "name")); | |
583 | key.algorithm = DNSName(stringFromJson(jsonKey, "algorithm")); | |
584 | key.key = stringFromJson(jsonKey, "content"); | |
585 | keys.push_back(key); | |
586 | } | |
587 | ||
588 | return true; | |
85f1a356 AT |
589 | } |
590 | ||
ff05c7e1 | 591 | void RemoteBackend::parseDomainInfo(const Json& obj, DomainInfo& di) |
e3991e7e | 592 | { |
ff05c7e1 O |
593 | di.id = intFromJson(obj, "id", -1); |
594 | di.zone = DNSName(stringFromJson(obj, "zone")); | |
d525b58b KM |
595 | for (const auto& primary : obj["masters"].array_items()) { |
596 | di.primaries.emplace_back(primary.string_value(), 53); | |
645d66cf | 597 | } |
ff05c7e1 O |
598 | |
599 | di.notified_serial = static_cast<unsigned int>(doubleFromJson(obj, "notified_serial", 0)); | |
600 | di.serial = static_cast<unsigned int>(obj["serial"].number_value()); | |
601 | di.last_check = static_cast<time_t>(obj["last_check"].number_value()); | |
602 | ||
645d66cf | 603 | string kind; |
ff05c7e1 O |
604 | if (obj["kind"].is_string()) { |
605 | kind = stringFromJson(obj, "kind"); | |
606 | } | |
607 | if (kind == "master") { | |
d525b58b | 608 | di.kind = DomainInfo::Primary; |
ff05c7e1 O |
609 | } |
610 | else if (kind == "slave") { | |
c02c999b | 611 | di.kind = DomainInfo::Secondary; |
ff05c7e1 O |
612 | } |
613 | else { | |
614 | di.kind = DomainInfo::Native; | |
615 | } | |
616 | di.backend = this; | |
e3991e7e AT |
617 | } |
618 | ||
d73de874 | 619 | bool RemoteBackend::getDomainInfo(const DNSName& domain, DomainInfo& di, bool /* getSerial */) |
ff05c7e1 | 620 | { |
645d66cf | 621 | if (domain.empty()) { |
ff05c7e1 | 622 | return false; |
645d66cf FM |
623 | } |
624 | ||
ff05c7e1 O |
625 | Json query = Json::object{ |
626 | {"method", "getDomainInfo"}, | |
627 | {"parameters", Json::object{{"name", domain.toString()}}}}; | |
628 | ||
629 | Json answer; | |
645d66cf | 630 | if (!this->send(query) || !this->recv(answer)) { |
ff05c7e1 | 631 | return false; |
645d66cf | 632 | } |
ff05c7e1 O |
633 | |
634 | this->parseDomainInfo(answer["result"], di); | |
635 | return true; | |
f4644dfc PD |
636 | } |
637 | ||
ff05c7e1 O |
638 | void RemoteBackend::setNotified(uint32_t id, uint32_t serial) |
639 | { | |
640 | Json query = Json::object{ | |
641 | {"method", "setNotified"}, | |
642 | {"parameters", Json::object{{"id", static_cast<double>(id)}, {"serial", static_cast<double>(serial)}}}}; | |
643 | ||
644 | Json answer; | |
645d66cf | 645 | if (!this->send(query) || !this->recv(answer)) { |
ff05c7e1 O |
646 | g_log << Logger::Error << kBackendId << " Failed to execute RPC for RemoteBackend::setNotified(" << id << "," << serial << ")" << endl; |
647 | } | |
e39adaaf BH |
648 | } |
649 | ||
d525b58b | 650 | bool RemoteBackend::autoPrimaryBackend(const string& ip, const DNSName& domain, const vector<DNSResourceRecord>& nsset, string* nameserver, string* account, DNSBackend** ddb) |
bbd3f8b2 | 651 | { |
ff05c7e1 O |
652 | Json::array rrset; |
653 | ||
654 | for (const auto& ns : nsset) { | |
655 | rrset.push_back(Json::object{ | |
d5fcd583 | 656 | {"qtype", ns.qtype.toString()}, |
ff05c7e1 | 657 | {"qname", ns.qname.toString()}, |
2b47a8d8 | 658 | {"qclass", QClass::IN.getCode()}, |
ff05c7e1 O |
659 | {"content", ns.content}, |
660 | {"ttl", static_cast<int>(ns.ttl)}, | |
661 | {"auth", ns.auth}}); | |
662 | } | |
663 | ||
664 | Json query = Json::object{ | |
665 | {"method", "superMasterBackend"}, | |
666 | {"parameters", Json::object{{"ip", ip}, {"domain", domain.toString()}, {"nsset", rrset}}}}; | |
667 | ||
645d66cf | 668 | *ddb = nullptr; |
ff05c7e1 O |
669 | |
670 | Json answer; | |
645d66cf | 671 | if (!this->send(query) || !this->recv(answer)) { |
ff05c7e1 | 672 | return false; |
645d66cf | 673 | } |
ff05c7e1 O |
674 | |
675 | // we are the backend | |
676 | *ddb = this; | |
677 | ||
678 | // we allow simple true as well... | |
679 | if (answer["result"].is_object()) { | |
680 | *account = stringFromJson(answer["result"], "account"); | |
681 | *nameserver = stringFromJson(answer["result"], "nameserver"); | |
682 | } | |
683 | ||
684 | return true; | |
bbd3f8b2 AT |
685 | } |
686 | ||
c02c999b | 687 | bool RemoteBackend::createSecondaryDomain(const string& ip, const DNSName& domain, const string& nameserver, const string& account) |
ff05c7e1 O |
688 | { |
689 | Json query = Json::object{ | |
690 | {"method", "createSlaveDomain"}, | |
691 | {"parameters", Json::object{ | |
692 | {"ip", ip}, | |
693 | {"domain", domain.toString()}, | |
694 | {"nameserver", nameserver}, | |
695 | {"account", account}, | |
696 | }}}; | |
697 | ||
698 | Json answer; | |
645d66cf | 699 | return this->send(query) && this->recv(answer); |
bbd3f8b2 AT |
700 | } |
701 | ||
ff05c7e1 O |
702 | bool RemoteBackend::replaceRRSet(uint32_t domain_id, const DNSName& qname, const QType& qtype, const vector<DNSResourceRecord>& rrset) |
703 | { | |
704 | Json::array json_rrset; | |
705 | for (const auto& rr : rrset) { | |
706 | json_rrset.push_back(Json::object{ | |
d5fcd583 | 707 | {"qtype", rr.qtype.toString()}, |
ff05c7e1 | 708 | {"qname", rr.qname.toString()}, |
2b47a8d8 | 709 | {"qclass", QClass::IN.getCode()}, |
ff05c7e1 O |
710 | {"content", rr.content}, |
711 | {"ttl", static_cast<int>(rr.ttl)}, | |
712 | {"auth", rr.auth}}); | |
713 | } | |
714 | ||
715 | Json query = Json::object{ | |
716 | {"method", "replaceRRSet"}, | |
d5fcd583 | 717 | {"parameters", Json::object{{"domain_id", static_cast<double>(domain_id)}, {"qname", qname.toString()}, {"qtype", qtype.toString()}, {"trxid", static_cast<double>(d_trxid)}, {"rrset", json_rrset}}}}; |
ff05c7e1 O |
718 | |
719 | Json answer; | |
645d66cf | 720 | return this->send(query) && this->recv(answer); |
bbd3f8b2 AT |
721 | } |
722 | ||
d73de874 | 723 | bool RemoteBackend::feedRecord(const DNSResourceRecord& rr, const DNSName& ordername, bool /* ordernameIsNSEC3 */) |
ff05c7e1 O |
724 | { |
725 | Json query = Json::object{ | |
726 | {"method", "feedRecord"}, | |
727 | {"parameters", Json::object{ | |
d5fcd583 | 728 | {"rr", Json::object{{"qtype", rr.qtype.toString()}, {"qname", rr.qname.toString()}, {"qclass", QClass::IN.getCode()}, {"content", rr.content}, {"ttl", static_cast<int>(rr.ttl)}, {"auth", rr.auth}, {"ordername", (ordername.empty() ? Json() : ordername.toString())}}}, |
ff05c7e1 O |
729 | {"trxid", static_cast<double>(d_trxid)}, |
730 | }}}; | |
731 | ||
732 | Json answer; | |
645d66cf | 733 | return this->send(query) && this->recv(answer); // XXX FIXME this API should not return 'true' I think -ahu |
bbd3f8b2 AT |
734 | } |
735 | ||
ff05c7e1 O |
736 | bool RemoteBackend::feedEnts(int domain_id, map<DNSName, bool>& nonterm) |
737 | { | |
738 | Json::array nts; | |
739 | ||
645d66cf | 740 | for (const auto& t : nonterm) { |
ff05c7e1 O |
741 | nts.push_back(Json::object{ |
742 | {"nonterm", t.first.toString()}, | |
743 | {"auth", t.second}}); | |
645d66cf | 744 | } |
ff05c7e1 O |
745 | |
746 | Json query = Json::object{ | |
747 | {"method", "feedEnts"}, | |
748 | {"parameters", Json::object{{"domain_id", domain_id}, {"trxid", static_cast<double>(d_trxid)}, {"nonterm", nts}}}, | |
749 | }; | |
750 | ||
751 | Json answer; | |
645d66cf | 752 | return this->send(query) && this->recv(answer); |
bbd3f8b2 AT |
753 | } |
754 | ||
ff05c7e1 O |
755 | bool RemoteBackend::feedEnts3(int domain_id, const DNSName& domain, map<DNSName, bool>& nonterm, const NSEC3PARAMRecordContent& ns3prc, bool narrow) |
756 | { | |
757 | Json::array nts; | |
758 | ||
645d66cf | 759 | for (const auto& t : nonterm) { |
ff05c7e1 O |
760 | nts.push_back(Json::object{ |
761 | {"nonterm", t.first.toString()}, | |
762 | {"auth", t.second}}); | |
645d66cf | 763 | } |
ff05c7e1 O |
764 | |
765 | Json query = Json::object{ | |
766 | {"method", "feedEnts3"}, | |
767 | {"parameters", Json::object{{"domain_id", domain_id}, {"domain", domain.toString()}, {"times", ns3prc.d_iterations}, {"salt", ns3prc.d_salt}, {"narrow", narrow}, {"trxid", static_cast<double>(d_trxid)}, {"nonterm", nts}}}, | |
768 | }; | |
769 | ||
770 | Json answer; | |
645d66cf | 771 | return this->send(query) && this->recv(answer); |
bbd3f8b2 AT |
772 | } |
773 | ||
ff05c7e1 O |
774 | bool RemoteBackend::startTransaction(const DNSName& domain, int domain_id) |
775 | { | |
645d66cf | 776 | this->d_trxid = time((time_t*)nullptr); |
ff05c7e1 O |
777 | |
778 | Json query = Json::object{ | |
779 | {"method", "startTransaction"}, | |
780 | {"parameters", Json::object{{"domain", domain.toString()}, {"domain_id", domain_id}, {"trxid", static_cast<double>(d_trxid)}}}}; | |
bbd3f8b2 | 781 | |
ff05c7e1 | 782 | Json answer; |
645d66cf | 783 | if (!this->send(query) || !this->recv(answer)) { |
ff05c7e1 O |
784 | d_trxid = -1; |
785 | return false; | |
786 | } | |
787 | return true; | |
bbd3f8b2 | 788 | } |
645d66cf | 789 | |
ff05c7e1 O |
790 | bool RemoteBackend::commitTransaction() |
791 | { | |
645d66cf | 792 | if (d_trxid == -1) { |
ff05c7e1 | 793 | return false; |
645d66cf | 794 | } |
ff05c7e1 O |
795 | |
796 | Json query = Json::object{ | |
797 | {"method", "commitTransaction"}, | |
798 | {"parameters", Json::object{{"trxid", static_cast<double>(d_trxid)}}}}; | |
799 | ||
800 | d_trxid = -1; | |
801 | Json answer; | |
645d66cf | 802 | return this->send(query) && this->recv(answer); |
bbd3f8b2 AT |
803 | } |
804 | ||
ff05c7e1 O |
805 | bool RemoteBackend::abortTransaction() |
806 | { | |
645d66cf | 807 | if (d_trxid == -1) { |
ff05c7e1 | 808 | return false; |
645d66cf | 809 | } |
ff05c7e1 O |
810 | |
811 | Json query = Json::object{ | |
812 | {"method", "abortTransaction"}, | |
813 | {"parameters", Json::object{{"trxid", static_cast<double>(d_trxid)}}}}; | |
814 | ||
815 | d_trxid = -1; | |
816 | Json answer; | |
645d66cf | 817 | return this->send(query) && this->recv(answer); |
bbd3f8b2 AT |
818 | } |
819 | ||
ff05c7e1 O |
820 | string RemoteBackend::directBackendCmd(const string& querystr) |
821 | { | |
822 | Json query = Json::object{ | |
823 | {"method", "directBackendCmd"}, | |
824 | {"parameters", Json::object{{"query", querystr}}}}; | |
fa2dd0e6 | 825 | |
ff05c7e1 | 826 | Json answer; |
645d66cf | 827 | if (!this->send(query) || !this->recv(answer)) { |
ff05c7e1 | 828 | return "backend command failed"; |
645d66cf | 829 | } |
e8ac9c39 | 830 | |
ff05c7e1 | 831 | return asString(answer["result"]); |
e8ac9c39 AT |
832 | } |
833 | ||
fd9af6ec | 834 | bool RemoteBackend::searchRecords(const string& pattern, size_t maxResults, vector<DNSResourceRecord>& result) |
efc5a846 | 835 | { |
fd9af6ec FM |
836 | const auto intMax = static_cast<decltype(maxResults)>(std::numeric_limits<int>::max()); |
837 | if (maxResults > intMax) { | |
838 | throw std::out_of_range("Remote backend: length of list of result (" + std::to_string(maxResults) + ") is larger than what the JSON library supports for serialization (" + std::to_string(intMax) + ")"); | |
839 | } | |
840 | ||
fa2dd0e6 | 841 | Json query = Json::object{ |
ff05c7e1 | 842 | {"method", "searchRecords"}, |
fd9af6ec | 843 | {"parameters", Json::object{{"pattern", pattern}, {"maxResults", static_cast<int>(maxResults)}}}}; |
fa2dd0e6 AT |
844 | |
845 | Json answer; | |
645d66cf | 846 | if (!this->send(query) || !this->recv(answer)) { |
fa2dd0e6 | 847 | return false; |
645d66cf | 848 | } |
efc5a846 | 849 | |
645d66cf | 850 | if (!answer["result"].is_array()) { |
fa2dd0e6 | 851 | return false; |
645d66cf | 852 | } |
efc5a846 | 853 | |
ff05c7e1 | 854 | for (const auto& row : answer["result"].array_items()) { |
fa2dd0e6 AT |
855 | DNSResourceRecord rr; |
856 | rr.qtype = stringFromJson(row, "qtype"); | |
857 | rr.qname = DNSName(stringFromJson(row, "qname")); | |
858 | rr.qclass = QClass::IN; | |
859 | rr.content = stringFromJson(row, "content"); | |
d77af44c | 860 | rr.ttl = row["ttl"].int_value(); |
fa2dd0e6 | 861 | rr.domain_id = intFromJson(row, "domain_id", -1); |
645d66cf FM |
862 | if (d_dnssec) { |
863 | rr.auth = (intFromJson(row, "auth", 1) != 0); | |
864 | } | |
865 | else { | |
fa2dd0e6 | 866 | rr.auth = 1; |
645d66cf | 867 | } |
d77af44c | 868 | rr.scopeMask = row["scopeMask"].int_value(); |
fa2dd0e6 | 869 | result.push_back(rr); |
efc5a846 AT |
870 | } |
871 | ||
872 | return true; | |
873 | } | |
874 | ||
fd9af6ec | 875 | bool RemoteBackend::searchComments(const string& /* pattern */, size_t /* maxResults */, vector<Comment>& /* result */) |
efc5a846 AT |
876 | { |
877 | // FIXME: Implement Comment API | |
878 | return false; | |
879 | } | |
2654e176 | 880 | |
d73de874 | 881 | void RemoteBackend::getAllDomains(vector<DomainInfo>* domains, bool /* getSerial */, bool include_disabled) |
e3991e7e AT |
882 | { |
883 | Json query = Json::object{ | |
ff05c7e1 O |
884 | {"method", "getAllDomains"}, |
885 | {"parameters", Json::object{{"include_disabled", include_disabled}}}}; | |
e3991e7e AT |
886 | |
887 | Json answer; | |
645d66cf | 888 | if (!this->send(query) || !this->recv(answer)) { |
e3991e7e | 889 | return; |
645d66cf | 890 | } |
e3991e7e | 891 | |
645d66cf | 892 | if (!answer["result"].is_array()) { |
e3991e7e | 893 | return; |
645d66cf | 894 | } |
ff05c7e1 O |
895 | |
896 | for (const auto& row : answer["result"].array_items()) { | |
e3991e7e AT |
897 | DomainInfo di; |
898 | this->parseDomainInfo(row, di); | |
899 | domains->push_back(di); | |
900 | } | |
901 | } | |
902 | ||
d525b58b | 903 | void RemoteBackend::getUpdatedPrimaries(vector<DomainInfo>& domains, std::unordered_set<DNSName>& /* catalogs */, CatalogHashMap& /* catalogHashes */) |
e99b4250 AT |
904 | { |
905 | Json query = Json::object{ | |
ff05c7e1 O |
906 | {"method", "getUpdatedMasters"}, |
907 | {"parameters", Json::object{}}, | |
e99b4250 AT |
908 | }; |
909 | ||
910 | Json answer; | |
645d66cf | 911 | if (!this->send(query) || !this->recv(answer)) { |
e99b4250 | 912 | return; |
645d66cf | 913 | } |
e99b4250 | 914 | |
645d66cf | 915 | if (!answer["result"].is_array()) { |
e99b4250 | 916 | return; |
645d66cf | 917 | } |
e99b4250 | 918 | |
ff05c7e1 | 919 | for (const auto& row : answer["result"].array_items()) { |
e99b4250 AT |
920 | DomainInfo di; |
921 | this->parseDomainInfo(row, di); | |
ddeea7a6 | 922 | domains.push_back(di); |
e99b4250 AT |
923 | } |
924 | } | |
925 | ||
c02c999b | 926 | void RemoteBackend::getUnfreshSecondaryInfos(vector<DomainInfo>* domains) |
ff05c7e1 | 927 | { |
dc4edd56 | 928 | Json query = Json::object{ |
ff05c7e1 O |
929 | {"method", "getUnfreshSlaveInfos"}, |
930 | {"parameters", Json::object{}}, | |
dc4edd56 PL |
931 | }; |
932 | ||
933 | Json answer; | |
645d66cf | 934 | if (!this->send(query) || !this->recv(answer)) { |
dc4edd56 | 935 | return; |
645d66cf | 936 | } |
dc4edd56 | 937 | |
645d66cf | 938 | if (!answer["result"].is_array()) { |
dc4edd56 | 939 | return; |
645d66cf | 940 | } |
dc4edd56 | 941 | |
ff05c7e1 | 942 | for (const auto& row : answer["result"].array_items()) { |
dc4edd56 PL |
943 | DomainInfo di; |
944 | this->parseDomainInfo(row, di); | |
945 | domains->push_back(di); | |
946 | } | |
947 | } | |
948 | ||
ce8c5899 KM |
949 | void RemoteBackend::setStale(uint32_t domain_id) |
950 | { | |
951 | Json query = Json::object{ | |
952 | {"method", "setStale"}, | |
953 | {"parameters", Json::object{{"id", static_cast<double>(domain_id)}}}}; | |
954 | ||
955 | Json answer; | |
645d66cf | 956 | if (!this->send(query) || !this->recv(answer)) { |
ce8c5899 KM |
957 | g_log << Logger::Error << kBackendId << " Failed to execute RPC for RemoteBackend::setStale(" << domain_id << ")" << endl; |
958 | } | |
959 | } | |
960 | ||
ff05c7e1 O |
961 | void RemoteBackend::setFresh(uint32_t domain_id) |
962 | { | |
963 | Json query = Json::object{ | |
964 | {"method", "setFresh"}, | |
965 | {"parameters", Json::object{{"id", static_cast<double>(domain_id)}}}}; | |
966 | ||
967 | Json answer; | |
645d66cf | 968 | if (!this->send(query) || !this->recv(answer)) { |
ff05c7e1 O |
969 | g_log << Logger::Error << kBackendId << " Failed to execute RPC for RemoteBackend::setFresh(" << domain_id << ")" << endl; |
970 | } | |
dc4edd56 PL |
971 | } |
972 | ||
ff05c7e1 | 973 | DNSBackend* RemoteBackend::maker() |
bf42c817 | 974 | { |
ff05c7e1 O |
975 | try { |
976 | return new RemoteBackend(); | |
977 | } | |
978 | catch (...) { | |
979 | g_log << Logger::Error << kBackendId << " Unable to instantiate a remotebackend!" << endl; | |
645d66cf | 980 | return nullptr; |
ff05c7e1 | 981 | }; |
bf42c817 PD |
982 | } |
983 | ||
984 | class RemoteBackendFactory : public BackendFactory | |
985 | { | |
ff05c7e1 O |
986 | public: |
987 | RemoteBackendFactory() : | |
988 | BackendFactory("remote") {} | |
989 | ||
990 | void declareArguments(const std::string& suffix = "") override | |
991 | { | |
992 | declare(suffix, "dnssec", "Enable dnssec support", "no"); | |
993 | declare(suffix, "connection-string", "Connection string", ""); | |
994 | } | |
995 | ||
996 | DNSBackend* make(const std::string& suffix = "") override | |
997 | { | |
998 | return new RemoteBackend(suffix); | |
999 | } | |
bf42c817 PD |
1000 | }; |
1001 | ||
1002 | class RemoteLoader | |
1003 | { | |
dfb9a585 | 1004 | public: |
ff05c7e1 | 1005 | RemoteLoader(); |
dfb9a585 CH |
1006 | }; |
1007 | ||
ff05c7e1 O |
1008 | RemoteLoader::RemoteLoader() |
1009 | { | |
1010 | BackendMakers().report(new RemoteBackendFactory); | |
1011 | g_log << Logger::Info << kBackendId << " This is the remote backend version " VERSION | |
5e6a3d93 | 1012 | #ifndef REPRODUCIBLE |
ff05c7e1 | 1013 | << " (" __DATE__ " " __TIME__ ")" |
5e6a3d93 | 1014 | #endif |
ff05c7e1 | 1015 | << " reporting" << endl; |
dfb9a585 | 1016 | } |
bf42c817 PD |
1017 | |
1018 | static RemoteLoader remoteloader; |