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