]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/ws-api.cc
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <boost/tokenizer.hpp>
27 #include <boost/circular_buffer.hpp>
28 #include "namespaces.hh"
32 #include "arguments.hh"
36 #include <sys/types.h>
39 extern string s_programname
;
42 #ifndef HAVE_STRCASESTR
45 * strcasestr() locates the first occurrence in the string s1 of the
46 * sequence of characters (excluding the terminating null character)
47 * in the string s2, ignoring case. strcasestr() returns a pointer
48 * to the located string, or a null pointer if the string is not found.
49 * If s2 is empty, the function returns s1.
53 strcasestr(const char *s1
, const char *s2
)
55 int *cm
= __trans_lower
;
56 const uchar_t
*us1
= (const uchar_t
*)s1
;
57 const uchar_t
*us2
= (const uchar_t
*)s2
;
61 if (us2
== NULL
|| *us2
== '\0')
65 while (*us1
!= '\0') {
66 if (c
== cm
[*us1
++]) {
68 while (cm
[c
= *++us2
] == cm
[*us1
++] && c
!= '\0')
71 return ((char *)tptr
- 1);
73 us2
= (const uchar_t
*)s2
;
81 #endif // HAVE_STRCASESTR
83 static Json
getServerDetail() {
86 { "id", "localhost" },
87 { "url", "/api/v1/servers/localhost" },
88 { "daemon_type", productTypeApiType() },
89 { "version", getPDNSVersion() },
90 { "config_url", "/api/v1/servers/localhost/config{/config_setting}" },
91 { "zones_url", "/api/v1/servers/localhost/zones{/zone}" }
95 /* Return information about the supported API versions.
96 * The format of this MUST NEVER CHANGE at it's not versioned.
98 void apiDiscovery(HttpRequest
* req
, HttpResponse
* resp
) {
99 if(req
->method
!= "GET")
100 throw HttpMethodNotAllowedException();
102 Json version1
= Json::object
{
106 Json doc
= Json::array
{ version1
};
111 void apiServer(HttpRequest
* req
, HttpResponse
* resp
) {
112 if(req
->method
!= "GET")
113 throw HttpMethodNotAllowedException();
115 Json doc
= Json::array
{getServerDetail()};
119 void apiServerDetail(HttpRequest
* req
, HttpResponse
* resp
) {
120 if(req
->method
!= "GET")
121 throw HttpMethodNotAllowedException();
123 resp
->setBody(getServerDetail());
126 void apiServerConfig(HttpRequest
* req
, HttpResponse
* resp
) {
127 if(req
->method
!= "GET")
128 throw HttpMethodNotAllowedException();
130 vector
<string
> items
= ::arg().list();
133 for(const string
& item
: items
) {
134 if(item
.find("password") != string::npos
|| item
.find("api-key") != string::npos
)
137 value
= ::arg()[item
];
139 doc
.push_back(Json::object
{
140 { "type", "ConfigSetting" },
148 static Json
logGrep(const string
& q
, const string
& fname
, const string
& prefix
)
150 FILE* ptr
= fopen(fname
.c_str(), "r");
152 throw ApiException("Opening \"" + fname
+ "\" failed: " + stringerror());
154 std::shared_ptr
<FILE> fp(ptr
, fclose
);
160 boost::replace_all(needle
, "%20", " ");
161 boost::replace_all(needle
, "%22", "\"");
163 boost::tokenizer
<boost::escaped_list_separator
<char> > t(needle
, boost::escaped_list_separator
<char>("\\", " ", "\""));
164 vector
<string
> matches(t
.begin(), t
.end());
165 matches
.push_back(prefix
);
167 boost::circular_buffer
<string
> lines(200);
168 while(stringfgets(fp
.get(), line
)) {
169 vector
<string
>::const_iterator iter
;
170 for(iter
= matches
.begin(); iter
!= matches
.end(); ++iter
) {
171 if(!strcasestr(line
.c_str(), iter
->c_str()))
174 if(iter
== matches
.end()) {
176 lines
.push_front(line
);
181 for(const string
& iline
: lines
) {
182 items
.push_back(iline
);
187 void apiServerSearchLog(HttpRequest
* req
, HttpResponse
* resp
) {
188 if(req
->method
!= "GET")
189 throw HttpMethodNotAllowedException();
191 string prefix
= " " + s_programname
+ "[";
192 resp
->setBody(logGrep(req
->getvars
["q"], ::arg()["api-logfile"], prefix
));
195 void apiServerStatistics(HttpRequest
* req
, HttpResponse
* resp
) {
196 if(req
->method
!= "GET")
197 throw HttpMethodNotAllowedException();
199 map
<string
,string
> items
;
200 productServerStatisticsFetch(items
);
203 typedef map
<string
, string
> items_t
;
204 for(const items_t::value_type
& item
: items
) {
205 doc
.push_back(Json::object
{
206 { "type", "StatisticItem" },
207 { "name", item
.first
},
208 { "value", item
.second
},
215 DNSName
apiNameToDNSName(const string
& name
) {
216 if (!isCanonical(name
)) {
217 throw ApiException("DNS Name '" + name
+ "' is not canonical");
220 return DNSName(name
);
222 throw ApiException("Unable to parse DNS Name '" + name
+ "'");
226 DNSName
apiZoneIdToName(const string
& id
) {
231 throw HttpBadRequestException();
233 std::size_t lastpos
= 0, pos
= 0;
234 while ((pos
= id
.find('=', lastpos
)) != string::npos
) {
235 ss
<< id
.substr(lastpos
, pos
-lastpos
);
238 if (id
[pos
+1] >= '0' && id
[pos
+1] <= '9') {
240 } else if (id
[pos
+1] >= 'A' && id
[pos
+1] <= 'F') {
241 c
= id
[pos
+1] - 'A' + 10;
243 throw HttpBadRequestException();
248 if (id
[pos
+2] >= '0' && id
[pos
+2] <= '9') {
249 c
+= id
[pos
+2] - '0';
250 } else if (id
[pos
+2] >= 'A' && id
[pos
+2] <= 'F') {
251 c
+= id
[pos
+2] - 'A' + 10;
253 throw HttpBadRequestException();
261 ss
<< id
.substr(lastpos
, pos
-lastpos
);
267 return DNSName(zonename
);
269 throw ApiException("Unable to parse DNS Name '" + zonename
+ "'");
273 string
apiZoneNameToId(const DNSName
& dname
) {
274 string name
=dname
.toString();
277 for(string::const_iterator iter
= name
.begin(); iter
!= name
.end(); ++iter
) {
278 if ((*iter
>= 'A' && *iter
<= 'Z') ||
279 (*iter
>= 'a' && *iter
<= 'z') ||
280 (*iter
>= '0' && *iter
<= '9') ||
281 (*iter
== '.') || (*iter
== '-')) {
284 ss
<< (boost::format("=%02X") % (int)(*iter
));
288 string id
= ss
.str();
291 if (id
.size() == 0 || id
.substr(id
.size()-1) != ".") {
295 // special handling for the root zone, as a dot on it's own doesn't work
298 id
= (boost::format("=%02X") % (int)('.')).str();
303 void apiCheckNameAllowedCharacters(const string
& name
) {
304 if (name
.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_/.-") != std::string::npos
)
305 throw ApiException("Name '"+name
+"' contains unsupported characters");
308 void apiCheckQNameAllowedCharacters(const string
& qname
) {
309 if (qname
.compare(0, 2, "*.") == 0) apiCheckNameAllowedCharacters(qname
.substr(2));
310 else apiCheckNameAllowedCharacters(qname
);