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