]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/ws.cc
link in yahttp
[thirdparty/pdns.git] / pdns / ws.cc
CommitLineData
12c86877 1/*
a2ce158c 2 Copyright (C) 2002 - 2012 PowerDNS.COM BV
12c86877
BH
3
4 This program is free software; you can redistribute it and/or modify
9054d8a4
BH
5 it under the terms of the GNU General Public License version 2
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"
12c86877 23#include "ws.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"
e611a06c 32#include "ueberbackend.hh"
dcc65f25 33#include <boost/format.hpp>
7b39c040 34#include <boost/foreach.hpp>
9ac4a7c6 35#include "namespaces.hh"
ca9fc6a1 36#include "rapidjson/document.h"
8537b9f0
BH
37#include "rapidjson/stringbuffer.h"
38#include "rapidjson/writer.h"
ba1a571d 39#include "version.hh"
12a82d65 40#include "session.hh"
8537b9f0
BH
41
42using namespace rapidjson;
12c86877
BH
43
44extern StatBag S;
45
ddc84d12
CH
46typedef map<string,string> varmap_t;
47
12c86877
BH
48StatWebServer::StatWebServer()
49{
50 d_start=time(0);
96d299db 51 d_min10=d_min5=d_min1=0;
c81c2ea8 52 d_ws = 0;
f17c93b4 53 d_tid = 0;
c81c2ea8
PD
54 if(arg().mustDo("webserver"))
55 d_ws = new WebServer(arg()["webserver-address"], arg().asNum("webserver-port"),arg()["webserver-password"]);
12c86877
BH
56}
57
58void StatWebServer::go()
59{
c81c2ea8
PD
60 if(arg().mustDo("webserver"))
61 {
62 S.doRings();
63 pthread_create(&d_tid, 0, threadHelper, this);
64 pthread_create(&d_tid, 0, statThreadHelper, this);
65 }
12c86877
BH
66}
67
12c86877
BH
68void StatWebServer::statThread()
69{
70 try {
71 for(;;) {
72 d_queries.submit(S.read("udp-queries"));
73 d_cachehits.submit(S.read("packetcache-hit"));
74 d_cachemisses.submit(S.read("packetcache-miss"));
75 d_qcachehits.submit(S.read("query-cache-hit"));
76 d_qcachemisses.submit(S.read("query-cache-miss"));
77 Utility::sleep(1);
78 }
79 }
80 catch(...) {
81 L<<Logger::Error<<"Webserver statThread caught an exception, dying"<<endl;
82 exit(1);
83 }
84}
85
86void *StatWebServer::statThreadHelper(void *p)
87{
88 StatWebServer *sws=static_cast<StatWebServer *>(p);
89 sws->statThread();
90 return 0; // never reached
91}
92
93
94void *StatWebServer::threadHelper(void *p)
95{
96 StatWebServer *sws=static_cast<StatWebServer *>(p);
97 sws->launch();
98 return 0; // never reached
99}
100
9f3fdaa0
CH
101static string htmlescape(const string &s) {
102 string result;
103 for(string::const_iterator it=s.begin(); it!=s.end(); ++it) {
104 switch (*it) {
105 case '&':
c86a96f9 106 result += "&amp;";
9f3fdaa0
CH
107 break;
108 case '<':
109 result += "&lt;";
110 break;
111 case '>':
112 result += "&gt;";
113 break;
114 default:
115 result += *it;
116 }
117 }
118 return result;
119}
120
12c86877
BH
121void printtable(ostringstream &ret, const string &ringname, const string &title, int limit=10)
122{
123 int tot=0;
124 int entries=0;
101b5d5d 125 vector<pair <string,unsigned int> >ring=S.getRing(ringname);
12c86877 126
1071abdd 127 for(vector<pair<string, unsigned int> >::const_iterator i=ring.begin(); i!=ring.end();++i) {
12c86877
BH
128 tot+=i->second;
129 entries++;
130 }
131
1071abdd
CH
132 ret<<"<div class=\"panel\">";
133 ret<<"<span class=resetring><i></i><a href=\"?resetring="<<ringname<<"\">Reset</a></span>"<<endl;
134 ret<<"<h2>"<<title<<"</h2>"<<endl;
135 ret<<"<div class=ringmeta>";
136 ret<<"<a class=topXofY href=\"?ring="<<ringname<<"\">Showing: Top "<<limit<<" of "<<entries<<"</a>"<<endl;
137 ret<<"<span class=resizering>Resize: ";
bb3c3f50 138 unsigned int sizes[]={10,100,500,1000,10000,500000,0};
12c86877
BH
139 for(int i=0;sizes[i];++i) {
140 if(S.getRingSize(ringname)!=sizes[i])
e2a77e08 141 ret<<"<a href=\"?resizering="<<ringname<<"&amp;size="<<sizes[i]<<"\">"<<sizes[i]<<"</a> ";
12c86877
BH
142 else
143 ret<<"("<<sizes[i]<<") ";
144 }
1071abdd 145 ret<<"</span></div>";
12c86877 146
1071abdd 147 ret<<"<table class=\"data\">";
12c86877 148 int printed=0;
f5cb7e61 149 int total=max(1,tot);
bb3c3f50 150 for(vector<pair<string,unsigned int> >::const_iterator i=ring.begin();limit && i!=ring.end();++i,--limit) {
9f3fdaa0 151 ret<<"<tr><td>"<<htmlescape(i->first)<<"</td><td>"<<i->second<<"</td><td align=right>"<< StatWebServer::makePercentage(i->second*100.0/total)<<"</td>"<<endl;
12c86877
BH
152 printed+=i->second;
153 }
154 ret<<"<tr><td colspan=3></td></tr>"<<endl;
155 if(printed!=tot)
f5cb7e61 156 ret<<"<tr><td><b>Rest:</b></td><td><b>"<<tot-printed<<"</b></td><td align=right><b>"<< StatWebServer::makePercentage((tot-printed)*100.0/total)<<"</b></td>"<<endl;
12c86877 157
e2a77e08 158 ret<<"<tr><td><b>Total:</b></td><td><b>"<<tot<<"</b></td><td align=right><b>100%</b></td>";
1071abdd 159 ret<<"</table></div>"<<endl;
12c86877
BH
160}
161
162void StatWebServer::printvars(ostringstream &ret)
163{
1071abdd 164 ret<<"<div class=panel><h2>Variables</h2><table class=\"data\">"<<endl;
12c86877
BH
165
166 vector<string>entries=S.getEntries();
167 for(vector<string>::const_iterator i=entries.begin();i!=entries.end();++i) {
168 ret<<"<tr><td>"<<*i<<"</td><td>"<<S.read(*i)<<"</td><td>"<<S.getDescrip(*i)<<"</td>"<<endl;
169 }
e2a77e08 170
1071abdd 171 ret<<"</table></div>"<<endl;
12c86877
BH
172}
173
174void StatWebServer::printargs(ostringstream &ret)
175{
e2a77e08 176 ret<<"<table border=1><tr><td colspan=3 bgcolor=\"#0000ff\"><font color=\"#ffffff\">Arguments</font></td>"<<endl;
12c86877
BH
177
178 vector<string>entries=arg().list();
179 for(vector<string>::const_iterator i=entries.begin();i!=entries.end();++i) {
180 ret<<"<tr><td>"<<*i<<"</td><td>"<<arg()[*i]<<"</td><td>"<<arg().getHelp(*i)<<"</td>"<<endl;
181 }
182}
183
b6f57093
BH
184string StatWebServer::makePercentage(const double& val)
185{
186 return (boost::format("%.01f%%") % val).str();
187}
188
02c04144 189string StatWebServer::indexfunction(HttpRequest* req, bool *custom)
12c86877 190{
02c04144 191 if(!req->queryArgs["resetring"].empty()){
12c86877 192 *custom=true;
02c04144 193 S.resetRing(req->queryArgs["resetring"]);
12c86877
BH
194 return "HTTP/1.1 301 Moved Permanently\nLocation: /\nConnection: close\n\n";
195 }
02c04144 196 if(!req->queryArgs["resizering"].empty()){
12c86877 197 *custom=true;
02c04144 198 S.resizeRing(req->queryArgs["resizering"], atoi(req->queryArgs["size"].c_str()));
12c86877
BH
199 return "HTTP/1.1 301 Moved Permanently\nLocation: /\nConnection: close\n\n";
200 }
201
202 ostringstream ret;
203
1071abdd
CH
204 ret<<"<!DOCTYPE html>"<<endl;
205 ret<<"<html><head>"<<endl;
206 ret<<"<title>PowerDNS Authoritative Server Monitor</title>"<<endl;
207 ret<<"<link rel=\"stylesheet\" href=\"style.css\"/>"<<endl;
208 ret<<"</head><body>"<<endl;
209
210 ret<<"<div class=\"row\">"<<endl;
211 ret<<"<div class=\"headl columns\">";
212 ret<<"<a href=\"/\" id=\"appname\">PowerDNS "VERSION;
213 if(!arg()["config-name"].empty()) {
214 ret<<" ["<<arg()["config-name"]<<"]";
215 }
216 ret<<"</a></div>"<<endl;
217 ret<<"<div class=\"headr columns\"></div></div>";
218 ret<<"<div class=\"row\"><div class=\"all columns\">";
12c86877
BH
219
220 time_t passed=time(0)-s_starttime;
221
e2a77e08
KM
222 ret<<"<p>Uptime: "<<
223 humanDuration(passed)<<
224 "<br>"<<endl;
12c86877 225
395b07ea 226 ret<<"Queries/second, 1, 5, 10 minute averages: "<<std::setprecision(3)<<
f6154a3b
CH
227 d_queries.get1()<<", "<<
228 d_queries.get5()<<", "<<
229 d_queries.get10()<<". Max queries/second: "<<d_queries.getMax()<<
12c86877
BH
230 "<br>"<<endl;
231
f6154a3b 232 if(d_cachemisses.get10()+d_cachehits.get10()>0)
b6f57093 233 ret<<"Cache hitrate, 1, 5, 10 minute averages: "<<
f6154a3b
CH
234 makePercentage((d_cachehits.get1()*100.0)/((d_cachehits.get1())+(d_cachemisses.get1())))<<", "<<
235 makePercentage((d_cachehits.get5()*100.0)/((d_cachehits.get5())+(d_cachemisses.get5())))<<", "<<
236 makePercentage((d_cachehits.get10()*100.0)/((d_cachehits.get10())+(d_cachemisses.get10())))<<
b6f57093 237 "<br>"<<endl;
12c86877 238
f6154a3b 239 if(d_qcachemisses.get10()+d_qcachehits.get10()>0)
395b07ea 240 ret<<"Backend query cache hitrate, 1, 5, 10 minute averages: "<<std::setprecision(2)<<
f6154a3b
CH
241 makePercentage((d_qcachehits.get1()*100.0)/((d_qcachehits.get1())+(d_qcachemisses.get1())))<<", "<<
242 makePercentage((d_qcachehits.get5()*100.0)/((d_qcachehits.get5())+(d_qcachemisses.get5())))<<", "<<
243 makePercentage((d_qcachehits.get10()*100.0)/((d_qcachehits.get10())+(d_qcachemisses.get10())))<<
b6f57093 244 "<br>"<<endl;
12c86877 245
395b07ea 246 ret<<"Backend query load, 1, 5, 10 minute averages: "<<std::setprecision(3)<<
f6154a3b
CH
247 d_qcachemisses.get1()<<", "<<
248 d_qcachemisses.get5()<<", "<<
249 d_qcachemisses.get10()<<". Max queries/second: "<<d_qcachemisses.getMax()<<
12c86877
BH
250 "<br>"<<endl;
251
1071abdd 252 ret<<"Total queries: "<<S.read("udp-queries")<<". Question/answer latency: "<<S.read("latency")/1000.0<<"ms</p><br>"<<endl;
02c04144 253 if(req->queryArgs["ring"].empty()) {
12c86877
BH
254 vector<string>entries=S.listRings();
255 for(vector<string>::const_iterator i=entries.begin();i!=entries.end();++i)
256 printtable(ret,*i,S.getRingTitle(*i));
257
f6154a3b 258 printvars(ret);
12c86877 259 if(arg().mustDo("webserver-print-arguments"))
f6154a3b 260 printargs(ret);
12c86877
BH
261 }
262 else
02c04144 263 printtable(ret,req->queryArgs["ring"],S.getRingTitle(req->queryArgs["ring"]),100);
12c86877 264
1071abdd 265 ret<<"</div></div>"<<endl;
ba1a571d 266 ret<<"<footer class=\"row\">"<<fullVersionString()<<"<br>&copy; 2013 <a href=\"http://www.powerdns.com/\">PowerDNS.COM BV</a>.</footer>"<<endl;
12c86877
BH
267 ret<<"</body></html>"<<endl;
268
269 return ret.str();
270}
271
6ed9291d 272static int intFromJson(const Value& val) {
12a82d65
CH
273 if (val.IsInt()) {
274 return val.GetInt();
275 } else if (val.IsString()) {
276 return atoi(val.GetString());
277 } else {
33196945 278 throw PDNSException("Value not an Integer");
12a82d65
CH
279 }
280}
9ac4a7c6 281
1abb81f4
CH
282static string getZone(const string& zonename) {
283 UeberBackend B;
1abb81f4 284 DomainInfo di;
73301d73
CH
285 if(!B.getDomainInfo(zonename, di))
286 return returnJSONError("Could not find domain '"+zonename+"'");
1abb81f4
CH
287
288 Document doc;
289 doc.SetObject();
290
291 Value root;
292 root.SetObject();
293 root.AddMember("name", zonename.c_str(), doc.GetAllocator());
294 root.AddMember("type", "Zone", doc.GetAllocator());
295 root.AddMember("kind", di.getKindString(), doc.GetAllocator());
296 Value masters;
297 masters.SetArray();
298 BOOST_FOREACH(const string& master, di.masters) {
299 Value value(master.c_str(), doc.GetAllocator());
300 masters.PushBack(value, doc.GetAllocator());
301 }
302 root.AddMember("masters", masters, doc.GetAllocator());
303 root.AddMember("serial", di.serial, doc.GetAllocator());
304 root.AddMember("notified_serial", di.notified_serial, doc.GetAllocator());
305 root.AddMember("last_check", (unsigned int) di.last_check, doc.GetAllocator());
306
307 DNSResourceRecord rr;
308 Value records;
309 records.SetArray();
3d89fc28
CH
310 di.backend->list(zonename, di.id);
311 while(di.backend->get(rr)) {
1abb81f4
CH
312 if (!rr.qtype.getCode())
313 continue; // skip empty non-terminals
314
315 Value object;
316 object.SetObject();
317 Value jname(rr.qname.c_str(), doc.GetAllocator()); // copy
318 object.AddMember("name", jname, doc.GetAllocator());
319 Value jtype(rr.qtype.getName().c_str(), doc.GetAllocator()); // copy
320 object.AddMember("type", jtype, doc.GetAllocator());
321 object.AddMember("ttl", rr.ttl, doc.GetAllocator());
322 object.AddMember("priority", rr.priority, doc.GetAllocator());
323 Value jcontent(rr.content.c_str(), doc.GetAllocator()); // copy
324 object.AddMember("content", jcontent, doc.GetAllocator());
325 records.PushBack(object, doc.GetAllocator());
326 }
327 root.AddMember("records", records, doc.GetAllocator());
328
329 doc.AddMember("zone", root, doc.GetAllocator());
330 return makeStringFromDocument(doc);
331}
332
838f3b98 333static string createOrUpdateZone(const string& zonename, bool onlyCreate, varmap_t& varmap) {
543843b3 334 UeberBackend B;
543843b3 335 DomainInfo di;
543843b3 336
3d89fc28 337 bool exists = B.getDomainInfo(zonename, di);
73301d73
CH
338 if(exists && onlyCreate)
339 return returnJSONError("Domain '"+zonename+"' already exists");
543843b3 340
3d89fc28 341 if(!exists) {
4d73ea84 342 if(!B.createDomain(zonename))
73301d73 343 return returnJSONError("Creating domain '"+zonename+"' failed");
543843b3 344
73301d73
CH
345 if(!B.getDomainInfo(zonename, di))
346 return returnJSONError("Creating domain '"+zonename+"' failed: lookup of domain ID failed");
543843b3 347
838f3b98
CH
348 // create SOA record so zone "really" exists
349 DNSResourceRecord soa;
350 soa.qname = zonename;
351 soa.content = "1";
352 soa.qtype = "SOA";
353 soa.domain_id = di.id;
354 soa.auth = 0;
355 soa.ttl = ::arg().asNum( "default-ttl" );
356 soa.priority = 0;
357
3d89fc28
CH
358 di.backend->startTransaction(zonename, di.id);
359 di.backend->feedRecord(soa);
360 di.backend->commitTransaction();
838f3b98 361 }
543843b3 362
3d89fc28
CH
363 di.backend->setKind(zonename, DomainInfo::stringToKind(varmap["kind"]));
364 di.backend->setMaster(zonename, varmap["master"]);
365
543843b3
CH
366 return getZone(zonename);
367}
368
c67bf8c5
CH
369static string apiServerConfig(HttpRequest* req) {
370 if(req->method != "GET")
371 throw HttpMethodNotAllowedException();
372
373 vector<string> items = ::arg().list();
374 Document doc;
375 doc.SetArray();
376 BOOST_FOREACH(const string& var, items) {
377 Value kv, key, value;
378 kv.SetArray();
379 key.SetString(var.c_str(), var.length());
380 kv.PushBack(key, doc.GetAllocator());
381
382 if(var.find("password") != string::npos)
383 value="*****";
384 else
385 value.SetString(::arg()[var].c_str(), ::arg()[var].length(), doc.GetAllocator());
386
387 kv.PushBack(value, doc.GetAllocator());
388 doc.PushBack(kv, doc.GetAllocator());
389 }
390 return makeStringFromDocument(doc);
391}
392
393static string apiServerSearchLog(HttpRequest* req) {
394 if(req->method != "GET")
395 throw HttpMethodNotAllowedException();
396
397 return makeLogGrepJSON(req->queryArgs["q"], ::arg()["experimental-logfile"], " pdns[");
398}
399
400static string apiServerZones(HttpRequest* req) {
401 if(req->method != "GET")
402 throw HttpMethodNotAllowedException();
403
404 UeberBackend B;
405 vector<DomainInfo> domains;
406 B.getAllDomains(&domains);
407
408 Document doc;
409 doc.SetObject();
410
411 Value jdomains;
412 jdomains.SetArray();
413
414 BOOST_FOREACH(const DomainInfo& di, domains) {
415 Value jdi;
416 jdi.SetObject();
417 jdi.AddMember("name", di.zone.c_str(), doc.GetAllocator());
418 jdi.AddMember("kind", di.getKindString(), doc.GetAllocator());
419 Value masters;
420 masters.SetArray();
421 BOOST_FOREACH(const string& master, di.masters) {
422 Value value(master.c_str(), doc.GetAllocator());
423 masters.PushBack(value, doc.GetAllocator());
424 }
425 jdi.AddMember("masters", masters, doc.GetAllocator());
426 jdi.AddMember("serial", di.serial, doc.GetAllocator());
427 jdi.AddMember("notified_serial", di.notified_serial, doc.GetAllocator());
428 jdi.AddMember("last_check", (unsigned int) di.last_check, doc.GetAllocator());
429 jdomains.PushBack(jdi, doc.GetAllocator());
430 }
431 doc.AddMember("domains", jdomains, doc.GetAllocator());
432 return makeStringFromDocument(doc);
433}
434
02c04144 435static string jsonDispatch(HttpRequest* req, const string& command) {
e611a06c 436 if(command=="get") {
02c04144 437 if(req->queryArgs.empty()) {
e611a06c
BH
438 vector<string> entries = S.getEntries();
439 BOOST_FOREACH(string& ent, entries) {
02c04144 440 req->queryArgs[ent];
e611a06c 441 }
02c04144
CH
442 req->queryArgs["version"];
443 req->queryArgs["uptime"];
e611a06c 444 }
7b39c040 445
e611a06c
BH
446 string variable, value;
447
8537b9f0
BH
448 Document doc;
449 doc.SetObject();
02c04144 450 for(varmap_t::const_iterator iter = req->queryArgs.begin(); iter != req->queryArgs.end() ; ++iter) {
e611a06c
BH
451 variable = iter->first;
452 if(variable == "version") {
ddc84d12 453 value = VERSION;
e611a06c
BH
454 }
455 else if(variable == "uptime") {
456 value = lexical_cast<string>(time(0) - s_starttime);
457 }
458 else
459 value = lexical_cast<string>(S.read(variable));
8537b9f0
BH
460 Value jval;
461 jval.SetString(value.c_str(), value.length(), doc.GetAllocator());
462 doc.AddMember(variable.c_str(), jval, doc.GetAllocator());
e611a06c 463 }
ddc84d12 464 return makeStringFromDocument(doc);
e611a06c 465 }
2fe9c01c 466 else if(command=="config") {
c67bf8c5 467 return apiServerConfig(req);
e611a06c 468 }
2fe9c01c 469 else if(command == "flush-cache") {
e611a06c
BH
470 extern PacketCache PC;
471 int number;
02c04144 472 if(req->queryArgs["domain"].empty())
e611a06c
BH
473 number = PC.purge();
474 else
02c04144 475 number = PC.purge(req->queryArgs["domain"]);
ac7ba905 476
e611a06c
BH
477 map<string, string> object;
478 object["number"]=lexical_cast<string>(number);
02c04144 479 //cerr<<"Flushed cache for '"<<queryArgs["domain"]<<"', cleaned "<<number<<" records"<<endl;
ddc84d12 480 return returnJSONObject(object);
e611a06c 481 }
2fe9c01c 482 else if(command == "pdns-control") {
02c04144 483 if(req->method!="POST")
33196945 484 throw HttpMethodNotAllowedException();
d267d1bf
BH
485 // cout<<"post: "<<post<<endl;
486 rapidjson::Document document;
02c04144 487 if(document.Parse<0>(req->body.c_str()).HasParseError())
73301d73 488 return returnJSONError("Unable to parse JSON");
d267d1bf
BH
489 // cout<<"Parameters: '"<<document["parameters"].GetString()<<"'\n";
490 vector<string> parameters;
491 stringtok(parameters, document["parameters"].GetString(), " \t");
492
493 DynListener::g_funk_t* ptr=0;
494 if(!parameters.empty())
495 ptr = DynListener::getFunc(toUpper(parameters[0]));
496 map<string, string> m;
497
498 if(ptr) {
499 m["result"] = (*ptr)(parameters, 0);
500 } else {
501 m["error"]="No such function "+toUpper(parameters[0]);
502 }
ddc84d12 503 return returnJSONObject(m);
d267d1bf 504 }
2fe9c01c 505 else if(command == "zone-rest") { // http://jsonstat?command=zone-rest&rest=/powerdns.nl/www.powerdns.nl/a
a2ce158c 506 vector<string> parts;
02c04144 507 stringtok(parts, req->queryArgs["rest"], "/");
a2ce158c 508 if(parts.size() != 3)
73301d73 509 return returnJSONError("Could not parse rest parameter");
a2ce158c
BH
510 UeberBackend B;
511 SOAData sd;
512 sd.db = (DNSBackend*)-1;
73301d73
CH
513 if(!B.getSOA(parts[0], sd) || !sd.db)
514 return returnJSONError("Could not find domain '"+parts[0]+"'");
a2ce158c
BH
515
516 QType qtype;
517 qtype=parts[2];
518 string qname=parts[1];
519 extern PacketCache PC;
520 PC.purge(qname);
521 // cerr<<"domain id: "<<sd.domain_id<<", lookup name: '"<<parts[1]<<"', for type: '"<<qtype.getName()<<"'"<<endl;
522
02c04144 523 if(req->method == "GET") {
a2ce158c
BH
524 B.lookup(qtype, parts[1], 0, sd.domain_id);
525
526 DNSResourceRecord rr;
ddc84d12 527 string ret = "{ \"records\": [";
a2ce158c
BH
528 map<string, string> object;
529 bool first=1;
530
531 while(B.get(rr)) {
2e76c05a
BH
532 if(!first) ret += ", ";
533 first=false;
534 object.clear();
535 object["name"] = rr.qname;
536 object["type"] = rr.qtype.getName();
537 object["ttl"] = lexical_cast<string>(rr.ttl);
538 object["priority"] = lexical_cast<string>(rr.priority);
539 object["content"] = rr.content;
540 ret+=returnJSONObject(object);
a2ce158c
BH
541 }
542 ret+="]}";
ddc84d12 543 return ret;
a2ce158c 544 }
02c04144 545 else if(req->method=="DELETE") {
a2ce158c
BH
546 sd.db->replaceRRSet(sd.domain_id, qname, qtype, vector<DNSResourceRecord>());
547
548 }
02c04144 549 else if(req->method=="POST") {
ca9fc6a1 550 rapidjson::Document document;
02c04144 551 if(document.Parse<0>(req->body.c_str()).HasParseError())
73301d73 552 return returnJSONError("Unable to parse JSON");
a2ce158c 553
a2ce158c
BH
554 DNSResourceRecord rr;
555 vector<DNSResourceRecord> rrset;
ca9fc6a1
BH
556 const rapidjson::Value &records= document["records"];
557 for(rapidjson::SizeType i = 0; i < records.Size(); ++i) {
558 const rapidjson::Value& record = records[i];
559 rr.qname=record["name"].GetString();
560 rr.content=record["content"].GetString();
561 rr.qtype=record["type"].GetString();
2e76c05a
BH
562 rr.domain_id = sd.domain_id;
563 rr.auth=0;
6ed9291d
CH
564 rr.ttl=intFromJson(record["ttl"]);
565 rr.priority=intFromJson(record["priority"]);
2e76c05a
BH
566
567 rrset.push_back(rr);
568
569 if(rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::SRV)
570 rr.content = lexical_cast<string>(rr.priority)+" "+rr.content;
571
572 try {
573 shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content));
574 string tmp=drc->serialize(rr.qname);
575 }
576 catch(std::exception& e)
577 {
232f0877 578 return returnJSONError("Following record had a problem: "+rr.qname+" IN " +rr.qtype.getName()+ " " + rr.content+": "+e.what());
2e76c05a 579 }
a2ce158c
BH
580 }
581 // but now what
582 sd.db->startTransaction(qname);
583 sd.db->replaceRRSet(sd.domain_id, qname, qtype, rrset);
584 sd.db->commitTransaction();
02c04144 585 return req->body;
a2ce158c
BH
586 }
587 }
ea12b64a 588 else if(command == "zone") {
02c04144 589 string zonename = req->queryArgs["zone"];
73301d73
CH
590 if (zonename.empty())
591 return returnJSONError("Must give zone parameter");
ea12b64a 592
02c04144 593 if(req->method == "GET") {
ea12b64a 594 // get current zone
a8f16540 595 return getZone(zonename);
02c04144 596 } else if (req->method == "POST") {
a8f16540 597 // create
02c04144
CH
598 return createOrUpdateZone(zonename, true, req->queryArgs);
599 } else if (req->method == "PUT") {
838f3b98 600 // update or create
02c04144
CH
601 return createOrUpdateZone(zonename, false, req->queryArgs);
602 } else if (req->method == "DELETE") {
9a889c55
CH
603 // delete
604 UeberBackend B;
9a889c55 605 DomainInfo di;
73301d73 606 if(!B.getDomainInfo(zonename, di))
232f0877 607 return returnJSONError("Deleting domain '"+zonename+"' failed: domain does not exist");
73301d73 608 if(!di.backend->deleteDomain(zonename))
232f0877 609 return returnJSONError("Deleting domain '"+zonename+"' failed: backend delete failed/unsupported");
9a889c55
CH
610 map<string, string> success; // empty success object
611 return returnJSONObject(success);
ea12b64a 612 } else {
33196945 613 throw HttpMethodNotAllowedException();
ea12b64a
CH
614 }
615 }
2fe9c01c 616 else if(command=="log-grep") {
c67bf8c5 617 return makeLogGrepJSON(req->queryArgs["needle"], ::arg()["experimental-logfile"], " pdns[");
9ac4a7c6 618 }
2fe9c01c 619 else if(command=="domains") {
c67bf8c5 620 return apiServerZones(req);
ddc84d12
CH
621 }
622
73301d73 623 return returnJSONError("No or unknown command given");
ddc84d12
CH
624}
625
c67bf8c5 626static string apiWrapper(boost::function<string(HttpRequest*)> handler, HttpRequest* req, bool *custom) {
ddc84d12
CH
627 *custom=1; // indicates we build the response
628 string ret="HTTP/1.1 200 OK\r\n"
629 "Server: PowerDNS/"VERSION"\r\n"
630 "Connection: close\r\n"
631 "Access-Control-Allow-Origin: *\r\n"
632 "Content-Type: application/json\r\n"
633 "\r\n" ;
634
ddc84d12 635 string callback;
ddc84d12 636
02c04144
CH
637 if(req->queryArgs.count("callback")) {
638 callback=req->queryArgs["callback"];
639 req->queryArgs.erase("callback");
e611a06c
BH
640 }
641
c67bf8c5 642 req->queryArgs.erase("_"); // jQuery cache buster
ddc84d12 643
ddc84d12
CH
644 if(!callback.empty())
645 ret += callback+"(";
646
c67bf8c5 647 ret += handler(req);
ddc84d12 648
e611a06c
BH
649 if(!callback.empty()) {
650 ret += ");";
ac7ba905 651 }
ac7ba905
BH
652 return ret;
653}
654
c67bf8c5
CH
655void StatWebServer::registerApiHandler(const string& url, boost::function<string(HttpRequest*)> handler) {
656 WebServer::HandlerFunction f = boost::bind(&apiWrapper, handler, _1, _2);
657 d_ws->registerHandler(url, f);
658}
659
660string StatWebServer::jsonstat(HttpRequest* req)
661{
662 string command;
663
664 if(req->queryArgs.count("command")) {
665 command=req->queryArgs["command"];
666 req->queryArgs.erase("command");
667 }
668
669 return jsonDispatch(req, command);
670}
671
02c04144 672string StatWebServer::cssfunction(HttpRequest* req, bool *custom)
1071abdd
CH
673{
674 *custom=1; // indicates we build the response
675 ostringstream ret;
676 ret<<"HTTP/1.1 200 OK\r\n"
677 "Server: PowerDNS/"VERSION"\r\n"
678 "Connection: close\r\n"
679 "Cache-Control: max-age=86400\r\n"
680 "Content-Type: text/css\r\n"
681 "\r\n";
682
683 ret<<"* { box-sizing: border-box; margin: 0; padding: 0; }"<<endl;
684 ret<<"body { color: black; background: white; margin-top: 1em; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 10pt; position: relative; }"<<endl;
685 ret<<"a { color: #0959c2; }"<<endl;
686 ret<<"a:hover { color: #3B8EC8; }"<<endl;
687 ret<<".row { width: 940px; max-width: 100%; min-width: 768px; margin: 0 auto; }"<<endl;
688 ret<<".row:before, .row:after { display: table; content:\" \"; }"<<endl;
689 ret<<".row:after { clear: both; }"<<endl;
690 ret<<".columns { position: relative; min-height: 1px; float: left; }"<<endl;
691 ret<<".all { width: 100%; }"<<endl;
692 ret<<".headl { width: 60%; }"<<endl;
693 ret<<".headr { width: 39.5%; float: right; background-repeat: no-repeat; margin-top: 7px; ";
694 ret<<"background-image: url(data:image/png;base64,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);";
695 ret<<" width: 154px; height: 20px; }"<<endl;
696 ret<<"a#appname { margin: 0; font-size: 27px; color: #666; text-decoration: none; font-weight: bold; display: block; }"<<endl;
697 ret<<"footer { border-top: 1px solid #ddd; padding-top: 4px; font-size: 12px; }"<<endl;
698 ret<<"footer.row { margin-top: 1em; margin-bottom: 1em; }"<<endl;
699 ret<<".panel { background: #f2f2f2; border: 1px solid #e6e6e6; margin: 0 0 22px 0; padding: 20px; }"<<endl;
700 ret<<"table.data { width: 100%; border-spacing: 0; border-top: 1px solid #333; }"<<endl;
701 ret<<"table.data td { border-bottom: 1px solid #333; padding: 2px; }"<<endl;
702 ret<<"table.data tr:nth-child(2n) { background: #e2e2e2; }"<<endl;
703 ret<<"table.data tr:hover { background: white; }"<<endl;
704 ret<<".ringmeta { margin-bottom: 5px; }"<<endl;
705 ret<<".resetring {float: right; }"<<endl;
706 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;
707 ret<<".resetring:hover i { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAA2ElEQVQY013PMUoDcRDF4c+kEzxCsNNCrBQvIGhnlcYm11EkBxAraw8gglgIoiJpAoKIYlBcgrgopsma3c3fwt1k9cHA480M8xvQp/nMjorOWY5ov7IAYlpjQk7aYxcuWBpwFQgJnUcaYk7GhEDIGL5w+MVpKLIRyR2b4JOjvGhUKzHTv2W7iuSN479Dvu9plf1awbQ6y3x1sU5tjpVJcMbakF6Ycoas8Dl5xEHJ160wRdfqzXfa6XQ4PLDlicWUjxHxZfndL/N+RhiwNzl/Q6PDhn/qsl76H7prcApk2B1aAAAAAElFTkSuQmCC);}"<<endl;
708 ret<<".resizering {float: right;}"<<endl;
709 return ret.str();
710}
711
12c86877
BH
712void StatWebServer::launch()
713{
714 try {
02c04144
CH
715 d_ws->registerHandler("/", boost::bind(&StatWebServer::indexfunction, this, _1, _2));
716 d_ws->registerHandler("/style.css", boost::bind(&StatWebServer::cssfunction, this, _1, _2));
c67bf8c5
CH
717 if(::arg().mustDo("experimental-json-interface")) {
718 registerApiHandler("/servers/localhost/config", &apiServerConfig);
719 registerApiHandler("/servers/localhost/search-log", &apiServerSearchLog);
720 registerApiHandler("/servers/localhost/zones", &apiServerZones);
721 // legacy dispatch
722 registerApiHandler("/jsonstat", boost::bind(&StatWebServer::jsonstat, this, _1));
723 }
96d299db 724 d_ws->go();
12c86877
BH
725 }
726 catch(...) {
727 L<<Logger::Error<<"StatWebserver thread caught an exception, dying"<<endl;
728 exit(1);
729 }
730}