]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/ws-api.cc
improve logging a bit, complete move to dnsname
[thirdparty/pdns.git] / pdns / ws-api.cc
CommitLineData
6ec5e728
CH
1/*
2 Copyright (C) 2002 - 2014 PowerDNS.COM BV
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License version 2
6 as published by the Free Software Foundation
7
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
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
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20*/
870a0fe4
AT
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
6ec5e728
CH
24#include <boost/foreach.hpp>
25#include <boost/tokenizer.hpp>
26#include <boost/circular_buffer.hpp>
27#include "namespaces.hh"
28#include "ws-api.hh"
29#include "json.hh"
6ec5e728
CH
30#include "version.hh"
31#include "arguments.hh"
32#include <stdio.h>
33#include <string.h>
34#include <ctype.h>
35#include <sys/types.h>
3c3c006b 36#include <iomanip>
6ec5e728 37
fd99e845
CH
38extern string s_programname;
39
6ec5e728
CH
40#ifndef HAVE_STRCASESTR
41
42/*
43 * strcasestr() locates the first occurrence in the string s1 of the
44 * sequence of characters (excluding the terminating null character)
45 * in the string s2, ignoring case. strcasestr() returns a pointer
46 * to the located string, or a null pointer if the string is not found.
47 * If s2 is empty, the function returns s1.
48 */
49
50static char *
51strcasestr(const char *s1, const char *s2)
52{
53 int *cm = __trans_lower;
54 const uchar_t *us1 = (const uchar_t *)s1;
55 const uchar_t *us2 = (const uchar_t *)s2;
56 const uchar_t *tptr;
57 int c;
58
59 if (us2 == NULL || *us2 == '\0')
60 return ((char *)us1);
61
62 c = cm[*us2];
63 while (*us1 != '\0') {
64 if (c == cm[*us1++]) {
65 tptr = us1;
66 while (cm[c = *++us2] == cm[*us1++] && c != '\0')
67 continue;
68 if (c == '\0')
69 return ((char *)tptr - 1);
70 us1 = tptr;
71 us2 = (const uchar_t *)s2;
72 c = cm[*us2];
73 }
74 }
75
76 return (NULL);
77}
78
79#endif // HAVE_STRCASESTR
80
81using namespace rapidjson;
82
83static void fillServerDetail(Value& out, Value::AllocatorType& allocator)
84{
85 Value jdaemonType(productTypeApiType().c_str(), allocator);
86 out.SetObject();
87 out.AddMember("type", "Server", allocator);
88 out.AddMember("id", "localhost", allocator);
89 out.AddMember("url", "/servers/localhost", allocator);
90 out.AddMember("daemon_type", jdaemonType, allocator);
509196af 91 Value jversion(getPDNSVersion().c_str(), allocator);
92 out.AddMember("version", jversion, allocator);
6ec5e728
CH
93 out.AddMember("config_url", "/servers/localhost/config{/config_setting}", allocator);
94 out.AddMember("zones_url", "/servers/localhost/zones{/zone}", allocator);
95}
96
97void apiServer(HttpRequest* req, HttpResponse* resp) {
98 if(req->method != "GET")
99 throw HttpMethodNotAllowedException();
100
101 Document doc;
102 doc.SetArray();
103 Value server;
104 fillServerDetail(server, doc.GetAllocator());
105 doc.PushBack(server, doc.GetAllocator());
669822d0 106 resp->setBody(doc);
6ec5e728
CH
107}
108
109void apiServerDetail(HttpRequest* req, HttpResponse* resp) {
110 if(req->method != "GET")
111 throw HttpMethodNotAllowedException();
112
113 Document doc;
114 fillServerDetail(doc, doc.GetAllocator());
669822d0 115 resp->setBody(doc);
6ec5e728
CH
116}
117
118void apiServerConfig(HttpRequest* req, HttpResponse* resp) {
119 if(req->method != "GET")
120 throw HttpMethodNotAllowedException();
121
122 vector<string> items = ::arg().list();
123 string value;
124 Document doc;
125 doc.SetArray();
126 BOOST_FOREACH(const string& item, items) {
127 Value jitem;
128 jitem.SetObject();
129 jitem.AddMember("type", "ConfigSetting", doc.GetAllocator());
130
131 Value jname(item.c_str(), doc.GetAllocator());
132 jitem.AddMember("name", jname, doc.GetAllocator());
133
134 if(item.find("password") != string::npos)
135 value = "***";
136 else
137 value = ::arg()[item];
138
139 Value jvalue(value.c_str(), doc.GetAllocator());
140 jitem.AddMember("value", jvalue, doc.GetAllocator());
141
142 doc.PushBack(jitem, doc.GetAllocator());
143 }
669822d0 144 resp->setBody(doc);
6ec5e728
CH
145}
146
147static string logGrep(const string& q, const string& fname, const string& prefix)
148{
149 FILE* ptr = fopen(fname.c_str(), "r");
150 if(!ptr) {
6825bb9a 151 throw ApiException("Opening \"" + fname + "\" failed: " + stringerror());
6ec5e728 152 }
dd7da6cd 153 std::shared_ptr<FILE> fp(ptr, fclose);
6ec5e728
CH
154
155 string line;
156 string needle = q;
157 trim_right(needle);
158
159 boost::replace_all(needle, "%20", " ");
160 boost::replace_all(needle, "%22", "\"");
161
162 boost::tokenizer<boost::escaped_list_separator<char> > t(needle, boost::escaped_list_separator<char>("\\", " ", "\""));
163 vector<string> matches(t.begin(), t.end());
164 matches.push_back(prefix);
165
166 boost::circular_buffer<string> lines(200);
167 while(stringfgets(fp.get(), line)) {
168 vector<string>::const_iterator iter;
169 for(iter = matches.begin(); iter != matches.end(); ++iter) {
170 if(!strcasestr(line.c_str(), iter->c_str()))
171 break;
172 }
173 if(iter == matches.end()) {
174 trim_right(line);
175 lines.push_front(line);
176 }
177 }
178
179 Document doc;
180 doc.SetArray();
181 if(!lines.empty()) {
182 BOOST_FOREACH(const string& line, lines) {
183 doc.PushBack(line.c_str(), doc.GetAllocator());
184 }
185 }
186 return makeStringFromDocument(doc);
187}
188
189void apiServerSearchLog(HttpRequest* req, HttpResponse* resp) {
190 if(req->method != "GET")
191 throw HttpMethodNotAllowedException();
192
fd99e845 193 string prefix = " " + s_programname + "[";
583ea80d 194 resp->body = logGrep(req->getvars["q"], ::arg()["experimental-logfile"], prefix);
6ec5e728
CH
195}
196
197void apiServerStatistics(HttpRequest* req, HttpResponse* resp) {
198 if(req->method != "GET")
199 throw HttpMethodNotAllowedException();
200
201 map<string,string> items;
202 productServerStatisticsFetch(items);
203
204 Document doc;
205 doc.SetArray();
206 typedef map<string, string> items_t;
f29b72dc 207 BOOST_FOREACH(const items_t::value_type& item, items) {
6ec5e728
CH
208 Value jitem;
209 jitem.SetObject();
210 jitem.AddMember("type", "StatisticItem", doc.GetAllocator());
211
212 Value jname(item.first.c_str(), doc.GetAllocator());
213 jitem.AddMember("name", jname, doc.GetAllocator());
214
215 Value jvalue(item.second.c_str(), doc.GetAllocator());
216 jitem.AddMember("value", jvalue, doc.GetAllocator());
217
218 doc.PushBack(jitem, doc.GetAllocator());
219 }
220
669822d0 221 resp->setBody(doc);
6ec5e728 222}
3c3c006b
CH
223
224string apiZoneIdToName(const string& id) {
225 string zonename;
226 ostringstream ss;
227
228 if(id.empty())
229 throw HttpBadRequestException();
230
231 std::size_t lastpos = 0, pos = 0;
232 while ((pos = id.find('=', lastpos)) != string::npos) {
233 ss << id.substr(lastpos, pos-lastpos);
1dbe38ba
CH
234 char c;
235 // decode tens
236 if (id[pos+1] >= '0' && id[pos+1] <= '9') {
237 c = id[pos+1] - '0';
238 } else if (id[pos+1] >= 'A' && id[pos+1] <= 'F') {
239 c = id[pos+1] - 'A' + 10;
3c3c006b
CH
240 } else {
241 throw HttpBadRequestException();
242 }
808bc955 243 c = c * 16;
1dbe38ba
CH
244
245 // decode unit place
246 if (id[pos+2] >= '0' && id[pos+2] <= '9') {
247 c += id[pos+2] - '0';
248 } else if (id[pos+2] >= 'A' && id[pos+2] <= 'F') {
249 c += id[pos+2] - 'A' + 10;
250 } else {
251 throw HttpBadRequestException();
252 }
253
254 ss << c;
3c3c006b
CH
255
256 lastpos = pos+3;
257 }
258 if (lastpos < pos) {
259 ss << id.substr(lastpos, pos-lastpos);
260 }
261
262 zonename = ss.str();
263
264 // strip trailing dot
406497f5 265 if (zonename.size() > 0 && zonename.substr(zonename.size()-1) == ".") {
161a8b69 266 zonename.resize(zonename.size()-1);
3c3c006b
CH
267 }
268 return zonename;
269}
270
271string apiZoneNameToId(const string& name) {
272 ostringstream ss;
273
274 for(string::const_iterator iter = name.begin(); iter != name.end(); ++iter) {
275 if ((*iter >= 'A' && *iter <= 'Z') ||
276 (*iter >= 'a' && *iter <= 'z') ||
277 (*iter >= '0' && *iter <= '9') ||
278 (*iter == '.') || (*iter == '-')) {
279 ss << *iter;
280 } else {
1dbe38ba 281 ss << (boost::format("=%02X") % (int)(*iter));
3c3c006b
CH
282 }
283 }
284
161a8b69
CH
285 string id = ss.str();
286
3c3c006b 287 // add trailing dot
406497f5 288 if (id.size() == 0 || id.substr(id.size()-1) != ".") {
161a8b69
CH
289 id += ".";
290 }
3c3c006b
CH
291
292 // special handling for the root zone, as a dot on it's own doesn't work
293 // everywhere.
294 if (id == ".") {
406497f5 295 id = (boost::format("=%02X") % (int)('.')).str();
3c3c006b
CH
296 }
297 return id;
298}