]>
Commit | Line | Data |
---|---|---|
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 |
38 | extern 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 | ||
50 | static char * | |
51 | strcasestr(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 | ||
81 | using namespace rapidjson; | |
82 | ||
83 | static 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 | ||
97 | void 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 | ||
109 | void 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 | ||
118 | void 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 | ||
147 | static 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 | ||
189 | void 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 | ||
197 | void 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 | |
224 | string 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 | ||
271 | string 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 | } |