]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/ws-auth.cc
Merge pull request #1312 from zeha/api-set-ptr
[thirdparty/pdns.git] / pdns / ws-auth.cc
CommitLineData
12c86877 1/*
6ec5e728 2 Copyright (C) 2002 - 2014 PowerDNS.COM BV
12c86877
BH
3
4 This program is free software; you can redistribute it and/or modify
6ec5e728 5 it under the terms of the GNU General Public License version 2
9054d8a4 6 as published by the Free Software Foundation
12c86877 7
f782fe38
MH
8 Additionally, the license of this program contains a special
9 exception which allows to distribute the program in binary form when
10 it is linked against OpenSSL.
11
12c86877
BH
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
06bd9ccf 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
9054d8a4
BH
20*/
21#include "utility.hh"
d267d1bf 22#include "dynlistener.hh"
2470b36e 23#include "ws-auth.hh"
e611a06c 24#include "json.hh"
12c86877
BH
25#include "webserver.hh"
26#include "logger.hh"
e611a06c 27#include "packetcache.hh"
12c86877
BH
28#include "statbag.hh"
29#include "misc.hh"
30#include "arguments.hh"
31#include "dns.hh"
6cc98ddf 32#include "comment.hh"
e611a06c 33#include "ueberbackend.hh"
dcc65f25 34#include <boost/format.hpp>
7b39c040 35#include <boost/foreach.hpp>
9ac4a7c6 36#include "namespaces.hh"
ca9fc6a1 37#include "rapidjson/document.h"
8537b9f0
BH
38#include "rapidjson/stringbuffer.h"
39#include "rapidjson/writer.h"
6ec5e728 40#include "ws-api.hh"
ba1a571d 41#include "version.hh"
3c3c006b
CH
42#include <iomanip>
43
44#ifdef HAVE_CONFIG_H
45# include <config.h>
46#endif // HAVE_CONFIG_H
8537b9f0
BH
47
48using namespace rapidjson;
12c86877
BH
49
50extern StatBag S;
51
dea47634 52AuthWebServer::AuthWebServer()
12c86877
BH
53{
54 d_start=time(0);
96d299db 55 d_min10=d_min5=d_min1=0;
c81c2ea8 56 d_ws = 0;
f17c93b4 57 d_tid = 0;
c81c2ea8
PD
58 if(arg().mustDo("webserver"))
59 d_ws = new WebServer(arg()["webserver-address"], arg().asNum("webserver-port"),arg()["webserver-password"]);
12c86877
BH
60}
61
dea47634 62void AuthWebServer::go()
12c86877 63{
c81c2ea8
PD
64 if(arg().mustDo("webserver"))
65 {
66 S.doRings();
dea47634 67 pthread_create(&d_tid, 0, webThreadHelper, this);
c81c2ea8
PD
68 pthread_create(&d_tid, 0, statThreadHelper, this);
69 }
12c86877
BH
70}
71
dea47634 72void AuthWebServer::statThread()
12c86877
BH
73{
74 try {
75 for(;;) {
76 d_queries.submit(S.read("udp-queries"));
77 d_cachehits.submit(S.read("packetcache-hit"));
78 d_cachemisses.submit(S.read("packetcache-miss"));
79 d_qcachehits.submit(S.read("query-cache-hit"));
80 d_qcachemisses.submit(S.read("query-cache-miss"));
81 Utility::sleep(1);
82 }
83 }
84 catch(...) {
85 L<<Logger::Error<<"Webserver statThread caught an exception, dying"<<endl;
86 exit(1);
87 }
88}
89
dea47634 90void *AuthWebServer::statThreadHelper(void *p)
12c86877 91{
dea47634
CH
92 AuthWebServer *self=static_cast<AuthWebServer *>(p);
93 self->statThread();
12c86877
BH
94 return 0; // never reached
95}
96
dea47634 97void *AuthWebServer::webThreadHelper(void *p)
12c86877 98{
dea47634
CH
99 AuthWebServer *self=static_cast<AuthWebServer *>(p);
100 self->webThread();
12c86877
BH
101 return 0; // never reached
102}
103
9f3fdaa0
CH
104static string htmlescape(const string &s) {
105 string result;
106 for(string::const_iterator it=s.begin(); it!=s.end(); ++it) {
107 switch (*it) {
108 case '&':
c86a96f9 109 result += "&amp;";
9f3fdaa0
CH
110 break;
111 case '<':
112 result += "&lt;";
113 break;
114 case '>':
115 result += "&gt;";
116 break;
117 default:
118 result += *it;
119 }
120 }
121 return result;
122}
123
12c86877
BH
124void printtable(ostringstream &ret, const string &ringname, const string &title, int limit=10)
125{
126 int tot=0;
127 int entries=0;
101b5d5d 128 vector<pair <string,unsigned int> >ring=S.getRing(ringname);
12c86877 129
1071abdd 130 for(vector<pair<string, unsigned int> >::const_iterator i=ring.begin(); i!=ring.end();++i) {
12c86877
BH
131 tot+=i->second;
132 entries++;
133 }
134
1071abdd
CH
135 ret<<"<div class=\"panel\">";
136 ret<<"<span class=resetring><i></i><a href=\"?resetring="<<ringname<<"\">Reset</a></span>"<<endl;
137 ret<<"<h2>"<<title<<"</h2>"<<endl;
138 ret<<"<div class=ringmeta>";
139 ret<<"<a class=topXofY href=\"?ring="<<ringname<<"\">Showing: Top "<<limit<<" of "<<entries<<"</a>"<<endl;
140 ret<<"<span class=resizering>Resize: ";
bb3c3f50 141 unsigned int sizes[]={10,100,500,1000,10000,500000,0};
12c86877
BH
142 for(int i=0;sizes[i];++i) {
143 if(S.getRingSize(ringname)!=sizes[i])
e2a77e08 144 ret<<"<a href=\"?resizering="<<ringname<<"&amp;size="<<sizes[i]<<"\">"<<sizes[i]<<"</a> ";
12c86877
BH
145 else
146 ret<<"("<<sizes[i]<<") ";
147 }
1071abdd 148 ret<<"</span></div>";
12c86877 149
1071abdd 150 ret<<"<table class=\"data\">";
12c86877 151 int printed=0;
f5cb7e61 152 int total=max(1,tot);
bb3c3f50 153 for(vector<pair<string,unsigned int> >::const_iterator i=ring.begin();limit && i!=ring.end();++i,--limit) {
dea47634 154 ret<<"<tr><td>"<<htmlescape(i->first)<<"</td><td>"<<i->second<<"</td><td align=right>"<< AuthWebServer::makePercentage(i->second*100.0/total)<<"</td>"<<endl;
12c86877
BH
155 printed+=i->second;
156 }
157 ret<<"<tr><td colspan=3></td></tr>"<<endl;
158 if(printed!=tot)
dea47634 159 ret<<"<tr><td><b>Rest:</b></td><td><b>"<<tot-printed<<"</b></td><td align=right><b>"<< AuthWebServer::makePercentage((tot-printed)*100.0/total)<<"</b></td>"<<endl;
12c86877 160
e2a77e08 161 ret<<"<tr><td><b>Total:</b></td><td><b>"<<tot<<"</b></td><td align=right><b>100%</b></td>";
1071abdd 162 ret<<"</table></div>"<<endl;
12c86877
BH
163}
164
dea47634 165void AuthWebServer::printvars(ostringstream &ret)
12c86877 166{
1071abdd 167 ret<<"<div class=panel><h2>Variables</h2><table class=\"data\">"<<endl;
12c86877
BH
168
169 vector<string>entries=S.getEntries();
170 for(vector<string>::const_iterator i=entries.begin();i!=entries.end();++i) {
171 ret<<"<tr><td>"<<*i<<"</td><td>"<<S.read(*i)<<"</td><td>"<<S.getDescrip(*i)<<"</td>"<<endl;
172 }
e2a77e08 173
1071abdd 174 ret<<"</table></div>"<<endl;
12c86877
BH
175}
176
dea47634 177void AuthWebServer::printargs(ostringstream &ret)
12c86877 178{
e2a77e08 179 ret<<"<table border=1><tr><td colspan=3 bgcolor=\"#0000ff\"><font color=\"#ffffff\">Arguments</font></td>"<<endl;
12c86877
BH
180
181 vector<string>entries=arg().list();
182 for(vector<string>::const_iterator i=entries.begin();i!=entries.end();++i) {
183 ret<<"<tr><td>"<<*i<<"</td><td>"<<arg()[*i]<<"</td><td>"<<arg().getHelp(*i)<<"</td>"<<endl;
184 }
185}
186
dea47634 187string AuthWebServer::makePercentage(const double& val)
b6f57093
BH
188{
189 return (boost::format("%.01f%%") % val).str();
190}
191
dea47634 192void AuthWebServer::indexfunction(HttpRequest* req, HttpResponse* resp)
12c86877 193{
ef1439ff
KM
194 if(!req->parameters["resetring"].empty()) {
195 if (S.ringExists(req->parameters["resetring"]))
196 S.resetRing(req->parameters["resetring"]);
80d59cd1
CH
197 resp->status = 301;
198 resp->headers["Location"] = "/";
199 return;
12c86877 200 }
80d59cd1 201 if(!req->parameters["resizering"].empty()){
ef1439ff
KM
202 int size=atoi(req->parameters["size"].c_str());
203 if (S.ringExists(req->parameters["resizering"]) && size > 0 && size <= 500000)
204 S.resizeRing(req->parameters["resizering"], atoi(req->parameters["size"].c_str()));
80d59cd1
CH
205 resp->status = 301;
206 resp->headers["Location"] = "/";
207 return;
12c86877
BH
208 }
209
210 ostringstream ret;
211
1071abdd
CH
212 ret<<"<!DOCTYPE html>"<<endl;
213 ret<<"<html><head>"<<endl;
214 ret<<"<title>PowerDNS Authoritative Server Monitor</title>"<<endl;
215 ret<<"<link rel=\"stylesheet\" href=\"style.css\"/>"<<endl;
216 ret<<"</head><body>"<<endl;
217
218 ret<<"<div class=\"row\">"<<endl;
219 ret<<"<div class=\"headl columns\">";
220 ret<<"<a href=\"/\" id=\"appname\">PowerDNS "VERSION;
221 if(!arg()["config-name"].empty()) {
222 ret<<" ["<<arg()["config-name"]<<"]";
223 }
224 ret<<"</a></div>"<<endl;
225 ret<<"<div class=\"headr columns\"></div></div>";
226 ret<<"<div class=\"row\"><div class=\"all columns\">";
12c86877
BH
227
228 time_t passed=time(0)-s_starttime;
229
e2a77e08
KM
230 ret<<"<p>Uptime: "<<
231 humanDuration(passed)<<
232 "<br>"<<endl;
12c86877 233
395b07ea 234 ret<<"Queries/second, 1, 5, 10 minute averages: "<<std::setprecision(3)<<
f6154a3b
CH
235 d_queries.get1()<<", "<<
236 d_queries.get5()<<", "<<
237 d_queries.get10()<<". Max queries/second: "<<d_queries.getMax()<<
12c86877
BH
238 "<br>"<<endl;
239
f6154a3b 240 if(d_cachemisses.get10()+d_cachehits.get10()>0)
b6f57093 241 ret<<"Cache hitrate, 1, 5, 10 minute averages: "<<
f6154a3b
CH
242 makePercentage((d_cachehits.get1()*100.0)/((d_cachehits.get1())+(d_cachemisses.get1())))<<", "<<
243 makePercentage((d_cachehits.get5()*100.0)/((d_cachehits.get5())+(d_cachemisses.get5())))<<", "<<
244 makePercentage((d_cachehits.get10()*100.0)/((d_cachehits.get10())+(d_cachemisses.get10())))<<
b6f57093 245 "<br>"<<endl;
12c86877 246
f6154a3b 247 if(d_qcachemisses.get10()+d_qcachehits.get10()>0)
395b07ea 248 ret<<"Backend query cache hitrate, 1, 5, 10 minute averages: "<<std::setprecision(2)<<
f6154a3b
CH
249 makePercentage((d_qcachehits.get1()*100.0)/((d_qcachehits.get1())+(d_qcachemisses.get1())))<<", "<<
250 makePercentage((d_qcachehits.get5()*100.0)/((d_qcachehits.get5())+(d_qcachemisses.get5())))<<", "<<
251 makePercentage((d_qcachehits.get10()*100.0)/((d_qcachehits.get10())+(d_qcachemisses.get10())))<<
b6f57093 252 "<br>"<<endl;
12c86877 253
395b07ea 254 ret<<"Backend query load, 1, 5, 10 minute averages: "<<std::setprecision(3)<<
f6154a3b
CH
255 d_qcachemisses.get1()<<", "<<
256 d_qcachemisses.get5()<<", "<<
257 d_qcachemisses.get10()<<". Max queries/second: "<<d_qcachemisses.getMax()<<
12c86877
BH
258 "<br>"<<endl;
259
1071abdd 260 ret<<"Total queries: "<<S.read("udp-queries")<<". Question/answer latency: "<<S.read("latency")/1000.0<<"ms</p><br>"<<endl;
80d59cd1 261 if(req->parameters["ring"].empty()) {
12c86877
BH
262 vector<string>entries=S.listRings();
263 for(vector<string>::const_iterator i=entries.begin();i!=entries.end();++i)
264 printtable(ret,*i,S.getRingTitle(*i));
265
f6154a3b 266 printvars(ret);
12c86877 267 if(arg().mustDo("webserver-print-arguments"))
f6154a3b 268 printargs(ret);
12c86877
BH
269 }
270 else
80d59cd1 271 printtable(ret,req->parameters["ring"],S.getRingTitle(req->parameters["ring"]),100);
12c86877 272
1071abdd 273 ret<<"</div></div>"<<endl;
ba1a571d 274 ret<<"<footer class=\"row\">"<<fullVersionString()<<"<br>&copy; 2013 <a href=\"http://www.powerdns.com/\">PowerDNS.COM BV</a>.</footer>"<<endl;
12c86877
BH
275 ret<<"</body></html>"<<endl;
276
80d59cd1 277 resp->body = ret.str();
12c86877
BH
278}
279
669822d0 280static void fillZone(const string& zonename, HttpResponse* resp) {
1abb81f4 281 UeberBackend B;
1abb81f4 282 DomainInfo di;
73301d73 283 if(!B.getDomainInfo(zonename, di))
669822d0 284 throw ApiException("Could not find domain '"+zonename+"'");
1abb81f4
CH
285
286 Document doc;
287 doc.SetObject();
288
418aa246 289 // id is the canonical lookup key, which doesn't actually match the name (in some cases)
3c3c006b
CH
290 string zoneId = apiZoneNameToId(di.zone);
291 Value jzoneId(zoneId.c_str(), doc.GetAllocator()); // copy
292 doc.AddMember("id", jzoneId, doc.GetAllocator());
293 string url = "/servers/localhost/zones/" + zoneId;
418aa246
CH
294 Value jurl(url.c_str(), doc.GetAllocator()); // copy
295 doc.AddMember("url", jurl, doc.GetAllocator());
296 doc.AddMember("name", di.zone.c_str(), doc.GetAllocator());
e2dba705
CH
297 doc.AddMember("type", "Zone", doc.GetAllocator());
298 doc.AddMember("kind", di.getKindString(), doc.GetAllocator());
1abb81f4
CH
299 Value masters;
300 masters.SetArray();
301 BOOST_FOREACH(const string& master, di.masters) {
302 Value value(master.c_str(), doc.GetAllocator());
303 masters.PushBack(value, doc.GetAllocator());
304 }
e2dba705
CH
305 doc.AddMember("masters", masters, doc.GetAllocator());
306 doc.AddMember("serial", di.serial, doc.GetAllocator());
307 doc.AddMember("notified_serial", di.notified_serial, doc.GetAllocator());
308 doc.AddMember("last_check", (unsigned int) di.last_check, doc.GetAllocator());
1abb81f4 309
6cc98ddf 310 // fill records
1abb81f4
CH
311 DNSResourceRecord rr;
312 Value records;
313 records.SetArray();
cea26350 314 di.backend->list(zonename, di.id, true); // incl. disabled
3d89fc28 315 while(di.backend->get(rr)) {
1abb81f4
CH
316 if (!rr.qtype.getCode())
317 continue; // skip empty non-terminals
318
319 Value object;
320 object.SetObject();
321 Value jname(rr.qname.c_str(), doc.GetAllocator()); // copy
322 object.AddMember("name", jname, doc.GetAllocator());
323 Value jtype(rr.qtype.getName().c_str(), doc.GetAllocator()); // copy
324 object.AddMember("type", jtype, doc.GetAllocator());
325 object.AddMember("ttl", rr.ttl, doc.GetAllocator());
326 object.AddMember("priority", rr.priority, doc.GetAllocator());
cea26350 327 object.AddMember("disabled", rr.disabled, doc.GetAllocator());
1abb81f4
CH
328 Value jcontent(rr.content.c_str(), doc.GetAllocator()); // copy
329 object.AddMember("content", jcontent, doc.GetAllocator());
330 records.PushBack(object, doc.GetAllocator());
331 }
e2dba705 332 doc.AddMember("records", records, doc.GetAllocator());
1abb81f4 333
6cc98ddf
CH
334 // fill comments
335 Comment comment;
336 Value comments;
337 comments.SetArray();
338 di.backend->listComments(di.id);
339 while(di.backend->getComment(comment)) {
340 Value object;
341 object.SetObject();
342 Value jname(comment.qname.c_str(), doc.GetAllocator()); // copy
343 object.AddMember("name", jname, doc.GetAllocator());
344 Value jtype(comment.qtype.getName().c_str(), doc.GetAllocator()); // copy
345 object.AddMember("type", jtype, doc.GetAllocator());
55f12d58 346 object.AddMember("modified_at", (unsigned int) comment.modified_at, doc.GetAllocator());
6cc98ddf
CH
347 Value jaccount(comment.account.c_str(), doc.GetAllocator()); // copy
348 object.AddMember("account", jaccount, doc.GetAllocator());
349 Value jcontent(comment.content.c_str(), doc.GetAllocator()); // copy
350 object.AddMember("content", jcontent, doc.GetAllocator());
351 comments.PushBack(object, doc.GetAllocator());
352 }
353 doc.AddMember("comments", comments, doc.GetAllocator());
354
669822d0 355 resp->setBody(doc);
1abb81f4
CH
356}
357
6ec5e728
CH
358void productServerStatisticsFetch(map<string,string>& out)
359{
a45303b8 360 vector<string> items = S.getEntries();
a45303b8 361 BOOST_FOREACH(const string& item, items) {
6ec5e728 362 out[item] = lexical_cast<string>(S.read(item));
a45303b8
CH
363 }
364
365 // add uptime
6ec5e728 366 out["uptime"] = lexical_cast<string>(time(0) - s_starttime);
c67bf8c5
CH
367}
368
6cc98ddf
CH
369static void apiServerZoneRRset(HttpRequest* req, HttpResponse* resp);
370
80d59cd1 371static void apiServerZones(HttpRequest* req, HttpResponse* resp) {
e2dba705
CH
372 UeberBackend B;
373 if (req->method == "POST") {
374 DomainInfo di;
375 Document document;
6ec5e728 376 req->json(document);
e2dba705
CH
377 string zonename = stringFromJson(document, "name");
378 // TODO: better validation of zonename
379 if(zonename.empty())
380 throw ApiException("Zone name empty");
381
382 string kind = stringFromJson(document, "kind");
e2dba705
CH
383
384 bool exists = B.getDomainInfo(zonename, di);
385 if(exists)
386 throw ApiException("Domain '"+zonename+"' already exists");
387
388 const Value &nameservers = document["nameservers"];
389 if (!nameservers.IsArray() || nameservers.Size() == 0)
390 throw ApiException("Need at least one nameserver");
391
7c0ba3d2
CH
392 string master;
393 const Value &masters = document["masters"];
394 if (masters.IsArray()) {
395 for (SizeType i = 0; i < masters.Size(); ++i) {
396 master += masters[i].GetString();
397 master += " ";
398 }
399 }
400
e2dba705
CH
401 // no going back after this
402 if(!B.createDomain(zonename))
403 throw ApiException("Creating domain '"+zonename+"' failed");
404
405 if(!B.getDomainInfo(zonename, di))
406 throw ApiException("Creating domain '"+zonename+"' failed: lookup of domain ID failed");
407
408 vector<DNSResourceRecord> rrset;
409
410 // create SOA record so zone "really" exists
411 DNSResourceRecord rr;
412 rr.qname = zonename;
39cb2a0d 413 rr.content = (boost::format("%s hostmaster@%s %d")
03f39ef9
CH
414 % nameservers[SizeType(0)].GetString()
415 % zonename
416 % intFromJson(document, "serial", 1)
e2dba705 417 ).str();
39cb2a0d
CH
418 SOAData sd;
419 fillSOAData(rr.content, sd);
420 rr.content = serializeSOAData(sd);
e2dba705
CH
421 rr.qtype = "SOA";
422 rr.domain_id = di.id;
f62559e1 423 rr.auth = 1;
e2dba705
CH
424 rr.ttl = ::arg().asNum( "default-ttl" );
425 rr.priority = 0;
426 rrset.push_back(rr);
427
7c0ba3d2 428 for (SizeType i = 0; i < nameservers.Size(); ++i) {
e2dba705
CH
429 rr.content = nameservers[i].GetString();
430 rr.qtype = "NS";
431 rrset.push_back(rr);
432 }
433
434 di.backend->startTransaction(zonename, di.id);
435 BOOST_FOREACH(rr, rrset) {
436 di.backend->feedRecord(rr);
437 }
438 di.backend->commitTransaction();
439
440 di.backend->setKind(zonename, DomainInfo::stringToKind(kind));
7c0ba3d2 441 di.backend->setMaster(zonename, master);
e2dba705 442
669822d0 443 fillZone(zonename, resp);
e2dba705
CH
444 return;
445 }
446
c67bf8c5
CH
447 if(req->method != "GET")
448 throw HttpMethodNotAllowedException();
449
c67bf8c5 450 vector<DomainInfo> domains;
cea26350 451 B.getAllDomains(&domains, true); // incl. disabled
c67bf8c5
CH
452
453 Document doc;
45de6290 454 doc.SetArray();
c67bf8c5
CH
455
456 BOOST_FOREACH(const DomainInfo& di, domains) {
457 Value jdi;
458 jdi.SetObject();
418aa246 459 // id is the canonical lookup key, which doesn't actually match the name (in some cases)
3c3c006b
CH
460 string zoneId = apiZoneNameToId(di.zone);
461 Value jzoneId(zoneId.c_str(), doc.GetAllocator()); // copy
462 jdi.AddMember("id", jzoneId, doc.GetAllocator());
463 string url = "/servers/localhost/zones/" + zoneId;
418aa246
CH
464 Value jurl(url.c_str(), doc.GetAllocator()); // copy
465 jdi.AddMember("url", jurl, doc.GetAllocator());
c67bf8c5
CH
466 jdi.AddMember("name", di.zone.c_str(), doc.GetAllocator());
467 jdi.AddMember("kind", di.getKindString(), doc.GetAllocator());
468 Value masters;
469 masters.SetArray();
470 BOOST_FOREACH(const string& master, di.masters) {
471 Value value(master.c_str(), doc.GetAllocator());
472 masters.PushBack(value, doc.GetAllocator());
473 }
474 jdi.AddMember("masters", masters, doc.GetAllocator());
475 jdi.AddMember("serial", di.serial, doc.GetAllocator());
476 jdi.AddMember("notified_serial", di.notified_serial, doc.GetAllocator());
477 jdi.AddMember("last_check", (unsigned int) di.last_check, doc.GetAllocator());
45de6290 478 doc.PushBack(jdi, doc.GetAllocator());
c67bf8c5 479 }
669822d0 480 resp->setBody(doc);
c67bf8c5
CH
481}
482
05776d2f 483static void apiServerZoneDetail(HttpRequest* req, HttpResponse* resp) {
3c3c006b 484 string zonename = apiZoneIdToName(req->path_parameters["id"]);
05776d2f 485
7c0ba3d2
CH
486 if(req->method == "PUT") {
487 // update domain settings
488 UeberBackend B;
489 DomainInfo di;
490 if(!B.getDomainInfo(zonename, di))
491 throw ApiException("Could not find domain '"+zonename+"'");
492
493 Document document;
6ec5e728 494 req->json(document);
7c0ba3d2
CH
495
496 string master;
497 const Value &masters = document["masters"];
498 if (masters.IsArray()) {
499 for(SizeType i = 0; i < masters.Size(); ++i) {
500 master += masters[i].GetString();
501 master += " ";
502 }
503 }
504
505 di.backend->setKind(zonename, DomainInfo::stringToKind(stringFromJson(document, "kind")));
506 di.backend->setMaster(zonename, master);
669822d0 507 fillZone(zonename, resp);
7c0ba3d2
CH
508 return;
509 }
a462a01d
CH
510 else if(req->method == "DELETE") {
511 // delete domain
512 UeberBackend B;
513 DomainInfo di;
514 if(!B.getDomainInfo(zonename, di))
515 throw ApiException("Could not find domain '"+zonename+"'");
516
517 if(!di.backend->deleteDomain(zonename))
518 throw ApiException("Deleting domain '"+zonename+"' failed: backend delete failed/unsupported");
519
520 // empty body on success
521 resp->body = "";
522 return;
6cc98ddf
CH
523 } else if (req->method == "PATCH") {
524 apiServerZoneRRset(req, resp);
525 return;
526 } else if (req->method == "GET") {
527 fillZone(zonename, resp);
528 return;
a462a01d 529 }
7c0ba3d2 530
6cc98ddf 531 throw HttpMethodNotAllowedException();
05776d2f
CH
532}
533
d1587ceb
CH
534static void makePtr(const DNSResourceRecord& rr, DNSResourceRecord* ptr) {
535 if (rr.qtype.getCode() == QType::A) {
536 uint32_t ip;
537 if (!IpToU32(rr.content, &ip)) {
538 throw ApiException("PTR: Invalid IP address given");
539 }
540 ptr->qname = (boost::format("%u.%u.%u.%u.in-addr.arpa")
541 % ((ip >> 24) & 0xff)
542 % ((ip >> 16) & 0xff)
543 % ((ip >> 8) & 0xff)
544 % ((ip ) & 0xff)
545 ).str();
546 } else if (rr.qtype.getCode() == QType::AAAA) {
547 ComboAddress ca(rr.content);
548 string tmp;
549 for (int group = 0; group < 8; ++group) {
550 tmp += (boost::format("%04x") % ntohs(ca.sin6.sin6_addr.s6_addr16[group])).str();
551 }
552 ostringstream ss;
553 size_t npos = tmp.size();
554 while (npos--) {
555 ss << tmp[npos] << ".";
556 }
557 ss << "ip6.arpa";
558 ptr->qname = ss.str();
559 } else {
560 throw ApiException("Unsupported PTR source '" + rr.qname + "' type '" + rr.qtype.getName() + "'");
561 }
562
563 ptr->qtype = "PTR";
564 ptr->ttl = rr.ttl;
565 ptr->disabled = rr.disabled;
566 ptr->priority = 0;
567 ptr->content = rr.qname;
568}
569
b3905a3d
CH
570static void apiServerZoneRRset(HttpRequest* req, HttpResponse* resp) {
571 if(req->method != "PATCH")
572 throw HttpMethodNotAllowedException();
573
574 UeberBackend B;
575 DomainInfo di;
3c3c006b 576 string zonename = apiZoneIdToName(req->path_parameters["id"]);
b3905a3d
CH
577 if(!B.getDomainInfo(zonename, di))
578 throw ApiException("Could not find domain '"+zonename+"'");
579
b3905a3d 580 Document document;
6ec5e728 581 req->json(document);
b3905a3d
CH
582
583 string qname, changetype;
584 QType qtype;
585 qname = stringFromJson(document, "name");
586 qtype = stringFromJson(document, "type");
587 changetype = toUpper(stringFromJson(document, "changetype"));
588
35f26cc5
CH
589 string dotsuffix = "." + zonename;
590 if(!iends_with(qname, dotsuffix) && qname != zonename)
591 throw ApiException("RRset "+qname+" IN "+qtype.getName()+": Name is out of zone");
592
b3905a3d 593 if (changetype == "DELETE") {
6cc98ddf
CH
594 // delete all matching qname/qtype RRs (and, implictly comments).
595 if (!di.backend->replaceRRSet(di.id, qname, qtype, vector<DNSResourceRecord>())) {
596 throw ApiException("Hosting backend does not support editing records.");
597 }
b3905a3d
CH
598 }
599 else if (changetype == "REPLACE") {
6cc98ddf
CH
600 vector<DNSResourceRecord> new_records;
601 vector<Comment> new_comments;
d1587ceb 602 vector<DNSResourceRecord> new_ptrs;
6cc98ddf
CH
603 bool replace_records = false;
604 bool replace_comments = false;
605
606 // gather records
b3905a3d 607 DNSResourceRecord rr;
b3905a3d 608 const Value& records = document["records"];
6cc98ddf
CH
609 if (records.IsArray()) {
610 replace_records = true;
611 for(SizeType idx = 0; idx < records.Size(); ++idx) {
612 const Value& record = records[idx];
613 rr.qname = stringFromJson(record, "name");
614 rr.content = stringFromJson(record, "content");
615 rr.qtype = stringFromJson(record, "type");
616 rr.domain_id = di.id;
617 rr.auth = 1;
618 rr.ttl = intFromJson(record, "ttl");
619 rr.priority = intFromJson(record, "priority");
620 rr.disabled = boolFromJson(record, "disabled");
621
622 if(rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::SRV)
623 rr.content = lexical_cast<string>(rr.priority)+" "+rr.content;
624
625 if(rr.qname != qname || rr.qtype != qtype)
626 throw ApiException("Record "+rr.qname+" IN "+rr.qtype.getName()+" "+rr.content+": Record bundled with wrong RRset");
627
628 try {
629 shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content));
630 string tmp = drc->serialize(rr.qname);
631 }
632 catch(std::exception& e)
633 {
634 throw ApiException("Record "+rr.qname+" IN "+rr.qtype.getName()+" "+rr.content+": "+e.what());
635 }
636
d1587ceb
CH
637 if ((rr.qtype.getCode() == QType::A || rr.qtype.getCode() == QType::AAAA) &&
638 boolFromJson(record, "set-ptr", false) == true) {
639 DNSResourceRecord ptr;
640 makePtr(rr, &ptr);
641
642 // verify that there's a zone for the PTR
643 DNSPacket fakePacket;
644 SOAData sd;
645 fakePacket.qtype = QType::PTR;
646 if (!B.getAuth(&fakePacket, &sd, ptr.qname, 0))
647 throw ApiException("Could not find domain for PTR '"+ptr.qname+"' requested for '"+ptr.content+"'");
648
649 ptr.domain_id = sd.domain_id;
650 new_ptrs.push_back(ptr);
651 }
652
6cc98ddf
CH
653 new_records.push_back(rr);
654 }
655 }
b3905a3d 656
6cc98ddf
CH
657 // gather comments
658 Comment c;
659 c.domain_id = di.id;
660 c.qname = qname;
661 c.qtype = qtype;
662 time_t now = time(0);
663 const Value& comments = document["comments"];
664 if (comments.IsArray()) {
665 replace_comments = true;
666 for(SizeType idx = 0; idx < comments.Size(); ++idx) {
667 const Value& comment = comments[idx];
668 c.modified_at = intFromJson(comment, "modified_at", now);
669 c.content = stringFromJson(comment, "content");
670 c.account = stringFromJson(comment, "account");
671 new_comments.push_back(c);
672 }
673 }
b3905a3d 674
6cc98ddf
CH
675 if (!replace_records && !replace_comments) {
676 throw ApiException("No change");
677 }
35f26cc5 678
6cc98ddf
CH
679 // Actually store the change(s).
680 di.backend->startTransaction(qname);
681 if (replace_records) {
682 if (!di.backend->replaceRRSet(di.id, qname, qtype, new_records)) {
683 throw ApiException("Hosting backend does not support editing records.");
b3905a3d 684 }
6cc98ddf
CH
685 }
686 if (replace_comments) {
687 if (!di.backend->replaceComments(di.id, qname, qtype, new_comments)) {
688 throw ApiException("Hosting backend does not support editing comments.");
b3905a3d
CH
689 }
690 }
cea26350 691 di.backend->commitTransaction();
d1587ceb
CH
692
693 // now the PTRs
694 BOOST_FOREACH(const DNSResourceRecord& rr, new_ptrs) {
695 DNSPacket fakePacket;
696 SOAData sd;
697 sd.db = (DNSBackend *)-1;
698 fakePacket.qtype = QType::PTR;
699
700 if (!B.getAuth(&fakePacket, &sd, rr.qname, 0))
701 throw ApiException("Could not find domain for PTR '"+rr.qname+"' requested for '"+rr.content+"' (while saving)");
702
703 sd.db->startTransaction(rr.qname);
704 if (!sd.db->replaceRRSet(sd.domain_id, rr.qname, rr.qtype, vector<DNSResourceRecord>(1, rr))) {
705 throw ApiException("PTR-Hosting backend does not support editing records.");
706 }
707 sd.db->commitTransaction();
708 }
709
b3905a3d
CH
710 }
711 else
712 throw ApiException("Changetype not understood");
713
714 extern PacketCache PC;
715 PC.purge(qname);
716
717 // success
718 resp->body = "{}";
719}
720
dea47634 721void AuthWebServer::jsonstat(HttpRequest* req, HttpResponse* resp)
80d59cd1
CH
722{
723 string command;
724
725 if(req->parameters.count("command")) {
726 command = req->parameters["command"];
727 req->parameters.erase("command");
728 }
729
a45303b8 730 if(command == "flush-cache") {
e611a06c
BH
731 extern PacketCache PC;
732 int number;
80d59cd1 733 if(req->parameters["domain"].empty())
e611a06c
BH
734 number = PC.purge();
735 else
80d59cd1 736 number = PC.purge(req->parameters["domain"]);
ac7ba905 737
e611a06c
BH
738 map<string, string> object;
739 object["number"]=lexical_cast<string>(number);
80d59cd1 740 //cerr<<"Flushed cache for '"<<parameters["domain"]<<"', cleaned "<<number<<" records"<<endl;
6ec5e728 741 resp->body = returnJsonObject(object);
80d59cd1 742 return;
e611a06c 743 }
2fe9c01c 744 else if(command == "pdns-control") {
02c04144 745 if(req->method!="POST")
33196945 746 throw HttpMethodNotAllowedException();
d267d1bf
BH
747 // cout<<"post: "<<post<<endl;
748 rapidjson::Document document;
6ec5e728 749 req->json(document);
d267d1bf
BH
750 // cout<<"Parameters: '"<<document["parameters"].GetString()<<"'\n";
751 vector<string> parameters;
752 stringtok(parameters, document["parameters"].GetString(), " \t");
753
754 DynListener::g_funk_t* ptr=0;
755 if(!parameters.empty())
756 ptr = DynListener::getFunc(toUpper(parameters[0]));
757 map<string, string> m;
758
759 if(ptr) {
760 m["result"] = (*ptr)(parameters, 0);
761 } else {
80d59cd1 762 resp->status = 404;
d267d1bf
BH
763 m["error"]="No such function "+toUpper(parameters[0]);
764 }
6ec5e728 765 resp->body = returnJsonObject(m);
80d59cd1 766 return;
d267d1bf 767 }
2fe9c01c 768 else if(command=="log-grep") {
6ec5e728
CH
769 // legacy parameter name hack
770 req->parameters["q"] = req->parameters["needle"];
771 apiServerSearchLog(req, resp);
80d59cd1 772 return;
9ac4a7c6 773 }
ddc84d12 774
6ec5e728 775 resp->body = returnJsonError("No or unknown command given");
80d59cd1
CH
776 resp->status = 404;
777 return;
ddc84d12
CH
778}
779
dea47634 780void AuthWebServer::cssfunction(HttpRequest* req, HttpResponse* resp)
c67bf8c5 781{
80d59cd1
CH
782 resp->headers["Cache-Control"] = "max-age=86400";
783 resp->headers["Content-Type"] = "text/css";
c67bf8c5 784
1071abdd 785 ostringstream ret;
1071abdd
CH
786 ret<<"* { box-sizing: border-box; margin: 0; padding: 0; }"<<endl;
787 ret<<"body { color: black; background: white; margin-top: 1em; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 10pt; position: relative; }"<<endl;
788 ret<<"a { color: #0959c2; }"<<endl;
789 ret<<"a:hover { color: #3B8EC8; }"<<endl;
790 ret<<".row { width: 940px; max-width: 100%; min-width: 768px; margin: 0 auto; }"<<endl;
791 ret<<".row:before, .row:after { display: table; content:\" \"; }"<<endl;
792 ret<<".row:after { clear: both; }"<<endl;
793 ret<<".columns { position: relative; min-height: 1px; float: left; }"<<endl;
794 ret<<".all { width: 100%; }"<<endl;
795 ret<<".headl { width: 60%; }"<<endl;
796 ret<<".headr { width: 39.5%; float: right; background-repeat: no-repeat; margin-top: 7px; ";
797 ret<<"background-image: url(data:image/png;base64,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);";
798 ret<<" width: 154px; height: 20px; }"<<endl;
799 ret<<"a#appname { margin: 0; font-size: 27px; color: #666; text-decoration: none; font-weight: bold; display: block; }"<<endl;
800 ret<<"footer { border-top: 1px solid #ddd; padding-top: 4px; font-size: 12px; }"<<endl;
801 ret<<"footer.row { margin-top: 1em; margin-bottom: 1em; }"<<endl;
802 ret<<".panel { background: #f2f2f2; border: 1px solid #e6e6e6; margin: 0 0 22px 0; padding: 20px; }"<<endl;
803 ret<<"table.data { width: 100%; border-spacing: 0; border-top: 1px solid #333; }"<<endl;
804 ret<<"table.data td { border-bottom: 1px solid #333; padding: 2px; }"<<endl;
805 ret<<"table.data tr:nth-child(2n) { background: #e2e2e2; }"<<endl;
806 ret<<"table.data tr:hover { background: white; }"<<endl;
807 ret<<".ringmeta { margin-bottom: 5px; }"<<endl;
808 ret<<".resetring {float: right; }"<<endl;
809 ret<<".resetring i { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAA/klEQVQY01XPP04UUBgE8N/33vd2XZUWEuzYuMZEG4KFCQn2NhA4AIewAOMBPIG2xhNYeAcKGqkNCdmYlVBZGBIT4FHsbuE0U8xk/kAbqm9TOfI/nicfhmwgDNhvylUT58kxCp4l31L8SfH9IetJ2ev6PwyIwyZWsdb11/gbTK55Co+r8rmJaRPTFJcpZil+pTit7C5awMpA+Zpi1sRFE9MqflYOloYCjY2uP8EdYiGU4CVGUBubxKfOOLjrtOBmzvEilbVb/aQWvhRl0unBZVXe4XdnK+bprwqnhoyTsyZ+JG8Wk0apfExxlcp7PFruXH8gdxamWB4cyW2sIO4BG3czIp78jUIAAAAASUVORK5CYII=); width: 10px; height: 10px; margin-right: 2px; display: inline-block; background-repeat: no-repeat; }"<<endl;
810 ret<<".resetring:hover i { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAA2ElEQVQY013PMUoDcRDF4c+kEzxCsNNCrBQvIGhnlcYm11EkBxAraw8gglgIoiJpAoKIYlBcgrgopsma3c3fwt1k9cHA480M8xvQp/nMjorOWY5ov7IAYlpjQk7aYxcuWBpwFQgJnUcaYk7GhEDIGL5w+MVpKLIRyR2b4JOjvGhUKzHTv2W7iuSN479Dvu9plf1awbQ6y3x1sU5tjpVJcMbakF6Ycoas8Dl5xEHJ160wRdfqzXfa6XQ4PLDlicWUjxHxZfndL/N+RhiwNzl/Q6PDhn/qsl76H7prcApk2B1aAAAAAElFTkSuQmCC);}"<<endl;
811 ret<<".resizering {float: right;}"<<endl;
80d59cd1 812 resp->body = ret.str();
1071abdd
CH
813}
814
dea47634 815void AuthWebServer::webThread()
12c86877
BH
816{
817 try {
c67bf8c5 818 if(::arg().mustDo("experimental-json-interface")) {
3ae143b0
CH
819 d_ws->registerApiHandler("/servers/localhost/config", &apiServerConfig);
820 d_ws->registerApiHandler("/servers/localhost/search-log", &apiServerSearchLog);
821 d_ws->registerApiHandler("/servers/localhost/statistics", &apiServerStatistics);
822 d_ws->registerApiHandler("/servers/localhost/zones/<id>/rrset", &apiServerZoneRRset);
823 d_ws->registerApiHandler("/servers/localhost/zones/<id>", &apiServerZoneDetail);
824 d_ws->registerApiHandler("/servers/localhost/zones", &apiServerZones);
825 d_ws->registerApiHandler("/servers/localhost", &apiServerDetail);
826 d_ws->registerApiHandler("/servers", &apiServer);
c67bf8c5 827 // legacy dispatch
dea47634 828 d_ws->registerApiHandler("/jsonstat", boost::bind(&AuthWebServer::jsonstat, this, _1, _2));
c67bf8c5 829 }
dea47634
CH
830 d_ws->registerHandler("/style.css", boost::bind(&AuthWebServer::cssfunction, this, _1, _2));
831 d_ws->registerHandler("/", boost::bind(&AuthWebServer::indexfunction, this, _1, _2));
96d299db 832 d_ws->go();
12c86877
BH
833 }
834 catch(...) {
dea47634 835 L<<Logger::Error<<"AuthWebServer thread caught an exception, dying"<<endl;
12c86877
BH
836 exit(1);
837 }
838}