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 "webserver.hh"
29 #include "threadname.hh"
37 #include "uuid-utils.hh"
38 #include <yahttp/router.hpp>
40 #include <unordered_set>
42 json11::Json
HttpRequest::json()
45 if(this->body
.empty()) {
46 SLOG(g_log
<<Logger::Debug
<<logprefix
<<"JSON document expected in request body, but body was empty" << endl
,
47 d_slog
->info(Logr::Debug
, "JSON document expected in request body, but body was empty"));
48 throw HttpBadRequestException();
50 json11::Json doc
= json11::Json::parse(this->body
, err
);
52 SLOG(g_log
<<Logger::Debug
<<logprefix
<<"parsing of JSON document failed:" << err
<< endl
,
53 d_slog
->error(Logr::Debug
, err
, "parsing of JSON document failed"));
54 throw HttpBadRequestException();
59 bool HttpRequest::compareAuthorization(const CredentialsHolder
& credentials
) const
62 auto header
= headers
.find("authorization");
64 if (header
!= headers
.end() && toLower(header
->second
).find("basic ") == 0) {
65 string cookie
= header
->second
.substr(6);
68 B64Decode(cookie
, plain
);
70 vector
<string
> cparts
;
71 stringtok(cparts
, plain
, ":");
73 auth_ok
= (cparts
.size() == 2 && credentials
.matches(cparts
[1].c_str()));
78 bool HttpRequest::compareHeader(const string
&header_name
, const string
&expected_value
) const
80 auto header
= headers
.find(header_name
);
81 if (header
== headers
.end()) {
85 // this gets rid of terminating zeros
86 return (0==strcmp(header
->second
.c_str(), expected_value
.c_str()));
89 bool HttpRequest::compareHeader(const string
&header_name
, const CredentialsHolder
& credentials
) const
91 auto header
= headers
.find(header_name
);
92 if (header
== headers
.end()) {
96 return credentials
.matches(header
->second
);
99 void HttpResponse::setPlainBody(const string
& document
)
101 this->headers
["Content-Type"] = "text/plain; charset=utf-8";
103 this->body
= document
;
106 void HttpResponse::setYamlBody(const string
& document
)
108 this->headers
["Content-Type"] = "application/x-yaml";
110 this->body
= document
;
113 void HttpResponse::setJsonBody(const string
& document
)
115 this->headers
["Content-Type"] = "application/json";
117 this->body
= document
;
120 void HttpResponse::setJsonBody(const json11::Json
& document
)
122 this->headers
["Content-Type"] = "application/json";
124 document
.dump(this->body
);
127 void HttpResponse::setErrorResult(const std::string
& message
, const int status_
)
129 setJsonBody(json11::Json::object
{ { "error", message
} });
130 this->status
= status_
;
133 void HttpResponse::setSuccessResult(const std::string
& message
, const int status_
)
135 setJsonBody(json11::Json::object
{ { "result", message
} });
136 this->status
= status_
;
139 static void bareHandlerWrapper(const WebServer::HandlerFunction
& handler
, YaHTTP::Request
* req
, YaHTTP::Response
* resp
)
141 // wrapper to convert from YaHTTP::* to our subclasses
142 handler(static_cast<HttpRequest
*>(req
), static_cast<HttpResponse
*>(resp
));
145 void WebServer::registerBareHandler(const string
& url
, const HandlerFunction
& handler
, const std::string
& method
)
147 YaHTTP::THandlerFunction f
= [=](YaHTTP::Request
* req
, YaHTTP::Response
* resp
){return bareHandlerWrapper(handler
, req
, resp
);};
148 YaHTTP::Router::Map(method
, url
, std::move(f
));
151 void WebServer::apiWrapper(const WebServer::HandlerFunction
& handler
, HttpRequest
* req
, HttpResponse
* resp
, bool allowPassword
) {
152 resp
->headers
["access-control-allow-origin"] = "*";
155 SLOG(g_log
<<Logger::Error
<<req
->logprefix
<<"HTTP API Request \"" << req
->url
.path
<< "\": Authentication failed, API Key missing in config" << endl
,
156 d_slog
->info(Logr::Error
, "Authentication failed, API Key missing in config", "urlpath", Logging::Loggable(req
->url
.path
)));
157 throw HttpUnauthorizedException("X-API-Key");
160 bool auth_ok
= req
->compareHeader("x-api-key", *d_apikey
) || d_apikey
->matches(req
->getvars
["api-key"]);
162 if (!auth_ok
&& allowPassword
) {
163 if (d_webserverPassword
) {
164 auth_ok
= req
->compareAuthorization(*d_webserverPassword
);
171 SLOG(g_log
<<Logger::Error
<<req
->logprefix
<<"HTTP Request \"" << req
->url
.path
<< "\": Authentication by API Key failed" << endl
,
172 d_slog
->info(Logr::Error
, "Authentication by API Key failed", "urlpath", Logging::Loggable(req
->url
.path
)));
173 throw HttpUnauthorizedException("X-API-Key");
177 resp
->headers
["X-Content-Type-Options"] = "nosniff";
178 resp
->headers
["X-Frame-Options"] = "deny";
179 resp
->headers
["X-Permitted-Cross-Domain-Policies"] = "none";
180 resp
->headers
["X-XSS-Protection"] = "1; mode=block";
181 resp
->headers
["Content-Security-Policy"] = "default-src 'self'; style-src 'self' 'unsafe-inline'";
183 req
->getvars
.erase("_"); // jQuery cache buster
188 } catch (ApiException
&e
) {
189 resp
->setErrorResult(e
.what(), 422);
191 } catch (JsonException
&e
) {
192 resp
->setErrorResult(e
.what(), 422);
196 if (resp
->status
== 204) {
197 // No Content -> no Content-Type.
198 resp
->headers
.erase("Content-Type");
202 void WebServer::registerApiHandler(const string
& url
, const HandlerFunction
& handler
, const std::string
& method
, bool allowPassword
) {
203 auto f
= [=](HttpRequest
*req
, HttpResponse
* resp
){apiWrapper(handler
, req
, resp
, allowPassword
);};
204 registerBareHandler(url
, f
, method
);
207 void WebServer::webWrapper(const WebServer::HandlerFunction
& handler
, HttpRequest
* req
, HttpResponse
* resp
) {
208 if (d_webserverPassword
) {
209 bool auth_ok
= req
->compareAuthorization(*d_webserverPassword
);
211 SLOG(g_log
<<Logger::Debug
<<req
->logprefix
<<"HTTP Request \"" << req
->url
.path
<< "\": Web Authentication failed" << endl
,
212 d_slog
->info(Logr::Debug
, "HTTP Request: Web Authentication failed", "urlpath", Logging::Loggable(req
->url
.path
)));
213 throw HttpUnauthorizedException("Basic");
220 void WebServer::registerWebHandler(const string
& url
, const HandlerFunction
& handler
, const std::string
& method
) {
221 auto f
= [=](HttpRequest
*req
, HttpResponse
*resp
){webWrapper(handler
, req
, resp
);};
222 registerBareHandler(url
, f
, method
);
225 static void* WebServerConnectionThreadStart(const WebServer
* webServer
, const std::shared_ptr
<Socket
>& client
)
227 setThreadName("rec/webhndlr");
228 const std::string msg
= "Exception while serving a connection in main webserver thread";
230 webServer
->serveConnection(client
);
232 catch(PDNSException
&e
) {
233 SLOG(g_log
<<Logger::Error
<<"PDNSException while serving a connection in main webserver thread: "<<e
.reason
<<endl
,
234 webServer
->d_slog
->error(Logr::Error
, e
.reason
, msg
, "exception", Logging::Loggable("PDNSException")));
236 catch(std::exception
&e
) {
237 SLOG(g_log
<<Logger::Error
<<"STL Exception while serving a connection in main webserver thread: "<<e
.what()<<endl
,
238 webServer
->d_slog
->error(Logr::Error
, e
.what(), msg
, "exception", Logging::Loggable("std::exception")));
241 SLOG(g_log
<<Logger::Error
<<"Unknown exception while serving a connection in main webserver thread"<<endl
,
242 webServer
->d_slog
->info(Logr::Error
, msg
));
247 void WebServer::handleRequest(HttpRequest
& req
, HttpResponse
& resp
) const
249 // set default headers
250 resp
.headers
["Content-Type"] = "text/html; charset=utf-8";
253 auto log
= req
.d_slog
->withValues("urlpath", Logging::Loggable(req
.url
.path
));
258 SLOG(g_log
<<Logger::Debug
<<req
.logprefix
<<"Incomplete request" << endl
,
259 d_slog
->info(Logr::Debug
, "Incomplete request"));
260 throw HttpBadRequestException();
262 SLOG(g_log
<<Logger::Debug
<<req
.logprefix
<<"Handling request \"" << req
.url
.path
<< "\"" << endl
,
263 log
->info(Logr::Debug
, "Handling request"));
265 YaHTTP::strstr_map_t::iterator header
;
267 if ((header
= req
.headers
.find("accept")) != req
.headers
.end()) {
268 // yaml wins over json, json wins over html
269 if (header
->second
.find("application/x-yaml") != std::string::npos
) {
270 req
.accept_yaml
= true;
271 } else if (header
->second
.find("text/x-yaml") != std::string::npos
) {
272 req
.accept_yaml
= true;
273 } else if (header
->second
.find("application/json") != std::string::npos
) {
274 req
.accept_json
= true;
275 } else if (header
->second
.find("text/html") != std::string::npos
) {
276 req
.accept_html
= true;
280 YaHTTP::THandlerFunction handler
;
281 YaHTTP::RoutingResult res
= YaHTTP::Router::Route(&req
, handler
);
283 if (res
== YaHTTP::RouteNotFound
) {
284 SLOG(g_log
<<Logger::Debug
<<req
.logprefix
<<"No route found for \"" << req
.url
.path
<< "\"" << endl
,
285 log
->info(Logr::Debug
, "No route found"));
286 throw HttpNotFoundException();
288 if (res
== YaHTTP::RouteNoMethod
) {
289 throw HttpMethodNotAllowedException();
292 const string msg
= "HTTP ISE Exception";
294 handler(&req
, &resp
);
295 SLOG(g_log
<<Logger::Debug
<<req
.logprefix
<<"Result for \"" << req
.url
.path
<< "\": " << resp
.status
<< ", body length: " << resp
.body
.size() << endl
,
296 log
->info(Logr::Debug
, "Result", "status", Logging::Loggable(resp
.status
), "bodyLength", Logging::Loggable(resp
.body
.size())));
298 catch(HttpException
&) {
301 catch(PDNSException
&e
) {
302 SLOG(g_log
<<Logger::Error
<<req
.logprefix
<<"HTTP ISE for \""<< req
.url
.path
<< "\": Exception: " << e
.reason
<< endl
,
303 log
->error(Logr::Error
, e
.reason
, msg
, "exception", Logging::Loggable("PDNSException")));
304 throw HttpInternalServerErrorException();
306 catch(std::exception
&e
) {
307 SLOG(g_log
<<Logger::Error
<<req
.logprefix
<<"HTTP ISE for \""<< req
.url
.path
<< "\": STL Exception: " << e
.what() << endl
,
308 log
->error(Logr::Error
, e
.what(), msg
, "exception", Logging::Loggable("std::exception")));
309 throw HttpInternalServerErrorException();
312 SLOG(g_log
<<Logger::Error
<<req
.logprefix
<<"HTTP ISE for \""<< req
.url
.path
<< "\": Unknown Exception" << endl
,
313 log
->info(Logr::Error
, msg
));
314 throw HttpInternalServerErrorException();
317 catch(HttpException
&e
) {
320 // An HttpException does not initialize d_slog
325 // TODO rm this logline?
326 SLOG(g_log
<<Logger::Debug
<<req
.logprefix
<<"Error result for \"" << req
.url
.path
<< "\": " << resp
.status
<< endl
,
327 d_slog
->error(Logr::Debug
, resp
.status
, "Error result", "urlpath", Logging::Loggable(req
.url
.path
)));
328 string what
= YaHTTP::Utility::status2text(resp
.status
);
329 if (req
.accept_json
) {
330 resp
.headers
["Content-Type"] = "application/json";
331 if (resp
.body
.empty()) {
332 resp
.setErrorResult(what
, resp
.status
);
334 } else if (req
.accept_html
) {
335 resp
.headers
["Content-Type"] = "text/html; charset=utf-8";
336 resp
.body
= "<!html><title>" + what
+ "</title><h1>" + what
+ "</h1>";
338 resp
.headers
["Content-Type"] = "text/plain; charset=utf-8";
339 resp
.body
= std::move(what
);
343 // always set these headers
344 resp
.headers
["Connection"] = "close";
346 if (req
.method
== "HEAD") {
349 resp
.headers
["Content-Length"] = std::to_string(resp
.body
.size());
354 // Helper to log key-value maps used by YaHTTP
356 std::string
Logging::IterLoggable
<YaHTTP::strstr_map_t::const_iterator
>::to_string() const
358 std::ostringstream oss
;
360 for (auto i
= _t1
; i
!= _t2
; i
++) {
367 oss
<< i
->first
<< ": " << i
->second
;
373 void WebServer::logRequest(const HttpRequest
& req
, [[maybe_unused
]] const ComboAddress
& remote
) const {
374 if (d_loglevel
>= WebServer::LogLevel::Detailed
) {
376 if (!g_slogStructured
) {
378 const auto& logprefix
= req
.logprefix
;
379 g_log
<<Logger::Notice
<<logprefix
<<"Request details:"<<endl
;
382 for (const auto& r
: req
.getvars
) {
385 g_log
<<Logger::Notice
<<logprefix
<<" GET params:"<<endl
;
387 g_log
<<Logger::Notice
<<logprefix
<<" "<<r
.first
<<": "<<r
.second
<<endl
;
391 for (const auto& r
: req
.postvars
) {
394 g_log
<<Logger::Notice
<<logprefix
<<" POST params:"<<endl
;
396 g_log
<<Logger::Notice
<<logprefix
<<" "<<r
.first
<<": "<<r
.second
<<endl
;
400 for (const auto& h
: req
.headers
) {
403 g_log
<<Logger::Notice
<<logprefix
<<" Headers:"<<endl
;
405 g_log
<<Logger::Notice
<<logprefix
<<" "<<h
.first
<<": "<<h
.second
<<endl
;
408 if (req
.body
.empty()) {
409 g_log
<<Logger::Notice
<<logprefix
<<" No body"<<endl
;
411 g_log
<<Logger::Notice
<<logprefix
<<" Full body: "<<endl
;
412 g_log
<<Logger::Notice
<<logprefix
<<" "<<req
.body
<<endl
;
417 req
.d_slog
->info(Logr::Info
, "Request details", "getParams", Logging::IterLoggable(req
.getvars
.cbegin(), req
.getvars
.cend()),
418 "postParams", Logging::IterLoggable(req
.postvars
.cbegin(), req
.postvars
.cend()),
419 "body", Logging::Loggable(req
.body
),
420 "address", Logging::Loggable(remote
));
426 void WebServer::logResponse(const HttpResponse
& resp
, const ComboAddress
& /* remote */, const string
& logprefix
) const {
427 if (d_loglevel
>= WebServer::LogLevel::Detailed
) {
429 if (!g_slogStructured
) {
431 g_log
<<Logger::Notice
<<logprefix
<<"Response details:"<<endl
;
433 for (const auto& h
: resp
.headers
) {
436 g_log
<<Logger::Notice
<<logprefix
<<" Headers:"<<endl
;
438 g_log
<<Logger::Notice
<<logprefix
<<" "<<h
.first
<<": "<<h
.second
<<endl
;
440 if (resp
.body
.empty()) {
441 g_log
<<Logger::Notice
<<logprefix
<<" No body"<<endl
;
443 g_log
<<Logger::Notice
<<logprefix
<<" Full body: "<<endl
;
444 g_log
<<Logger::Notice
<<logprefix
<<" "<<resp
.body
<<endl
;
449 resp
.d_slog
->info(Logr::Info
, "Response details", "headers", Logging::IterLoggable(resp
.headers
.cbegin(), resp
.headers
.cend()),
450 "body", Logging::Loggable(resp
.body
));
460 // letter may be signed, but we only pass positive values
461 for (auto letter
: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=") {
465 std::bitset
<127> set
;
468 static const ValidChars validChars
;
470 static bool validURLChars(const string
& str
)
472 for (auto iter
= str
.begin(); iter
!= str
.end(); ++iter
) {
475 if (iter
== str
.end() || isxdigit(static_cast<unsigned char>(*iter
)) == 0) {
479 if (iter
== str
.end() || isxdigit(static_cast<unsigned char>(*iter
)) == 0) {
483 else if (static_cast<size_t>(*iter
) >= validChars
.set
.size() || !validChars
.set
[*iter
]) {
490 bool WebServer::validURL(const YaHTTP::URL
& url
)
493 isOK
= isOK
&& validURLChars(url
.protocol
);
494 isOK
= isOK
&& validURLChars(url
.host
);
495 isOK
= isOK
&& validURLChars(url
.username
);
496 isOK
= isOK
&& validURLChars(url
.password
);
497 isOK
= isOK
&& validURLChars(url
.path
);
498 isOK
= isOK
&& validURLChars(url
.parameters
);
499 isOK
= isOK
&& validURLChars(url
.anchor
);
503 void WebServer::serveConnection(const std::shared_ptr
<Socket
>& client
) const {
504 const auto unique
= getUniqueID();
505 const string logprefix
= d_logprefix
+ to_string(unique
) + " ";
507 HttpRequest
req(logprefix
);
511 auto log
= d_slog
->withValues("uniqueid", Logging::Loggable(to_string(unique
)));
515 resp
.max_response_size
=d_maxbodysize
;
520 YaHTTP::AsyncRequestLoader yarl
;
521 yarl
.initialize(&req
);
522 req
.max_request_size
=d_maxbodysize
;
524 client
->setNonBlocking();
527 while(!req
.complete
) {
530 bytes
= client
->readWithTimeout(buf
, sizeof(buf
), timeout
);
532 string data
= string(buf
, bytes
);
533 req
.complete
= yarl
.feed(data
);
540 } catch (YaHTTP::ParseError
&e
) {
541 // request stays incomplete
542 SLOG(g_log
<<Logger::Warning
<<logprefix
<<"Unable to parse request: "<<e
.what()<<endl
,
543 d_slog
->error(Logr::Warning
, e
.what(), "Unable to parse request"));
546 if (!validURL(req
.url
)) {
547 throw PDNSException("Received request with invalid URL");
549 // Uses of `remote` below guarded by d_loglevel
550 if (d_loglevel
> WebServer::LogLevel::None
) {
551 client
->getRemote(remote
);
554 logRequest(req
, remote
);
556 WebServer::handleRequest(req
, resp
);
561 logResponse(resp
, remote
, logprefix
);
563 client
->writenWithTimeout(reply
.c_str(), reply
.size(), timeout
);
565 catch(PDNSException
&e
) {
566 SLOG(g_log
<<Logger::Error
<<logprefix
<<"HTTP Exception: "<<e
.reason
<<endl
,
567 d_slog
->error(Logr::Error
, e
.reason
, "HTTP Exception", "exception", Logging::Loggable("PDNSException")));
569 catch(std::exception
&e
) {
570 if(strstr(e
.what(), "timeout")==nullptr)
571 SLOG(g_log
<<Logger::Error
<<logprefix
<<"HTTP STL Exception: "<<e
.what()<<endl
,
572 d_slog
->error(Logr::Error
, e
.what(), "HTTP Exception", "exception", Logging::Loggable("std::exception")));
575 SLOG(g_log
<<Logger::Error
<<logprefix
<<"Unknown exception"<<endl
,
576 d_slog
->info(Logr::Error
, "HTTP Exception"));
579 if (d_loglevel
>= WebServer::LogLevel::Normal
) {
580 SLOG(g_log
<<Logger::Notice
<<logprefix
<<remote
<<" \""<<req
.method
<<" "<<req
.url
.path
<<" HTTP/"<<req
.versionStr(req
.version
)<<"\" "<<resp
.status
<<" "<<reply
.size()<<endl
,
581 d_slog
->info(Logr::Info
, "Request", "remote", Logging::Loggable(remote
), "method", Logging::Loggable(req
.method
),
582 "urlpath", Logging::Loggable(req
.url
.path
), "HTTPVersion", Logging::Loggable(req
.versionStr(req
.version
)),
583 "status", Logging::Loggable(resp
.status
), "respsize", Logging::Loggable(reply
.size())));
587 WebServer::WebServer(string listenaddress
, int port
) :
588 d_listenaddress(std::move(listenaddress
)),
591 d_maxbodysize(2*1024*1024)
593 YaHTTP::Router::Map("OPTIONS", "/<*url>", [](YaHTTP::Request
*req
, YaHTTP::Response
*resp
) {
594 // look for url in routes
596 std::vector
<std::string
> methods
;
597 for(const auto& route
: YaHTTP::Router::GetRoutes()) {
598 const auto& method
= std::get
<0>(route
);
599 const auto& url
= std::get
<1>(route
);
600 if (method
== "OPTIONS") {
603 std::map
<std::string
, YaHTTP::TDelim
> params
;
604 if (YaHTTP::Router::Match(url
, req
->url
, params
)) {
605 methods
.push_back(method
);
614 methods
.emplace_back("OPTIONS");
615 resp
->headers
["access-control-allow-origin"] = "*";
616 resp
->headers
["access-control-allow-headers"] = "Content-Type, X-API-Key";
617 resp
->headers
["access-control-allow-methods"] = boost::algorithm::join(methods
, ", ");
618 resp
->headers
["access-control-max-age"] = "3600";
620 resp
->headers
["content-type"]= "text/plain";
622 }, "OptionsHandlerRoute");
625 void WebServer::bind()
628 d_server
= createServer();
629 SLOG(g_log
<<Logger::Warning
<<d_logprefix
<<"Listening for HTTP requests on "<<d_server
->d_local
.toStringWithPort()<<endl
,
630 d_slog
->info(Logr::Info
, "Listening for HTTP requests", "address", Logging::Loggable(d_server
->d_local
)));
632 catch(NetworkError
&e
) {
633 SLOG(g_log
<<Logger::Error
<<d_logprefix
<<"Listening on HTTP socket failed: "<<e
.what()<<endl
,
634 d_slog
->error(Logr::Error
, e
.what(), "Listening on HTTP socket failed", "exception", Logging::Loggable("NetworkError")));
643 const string msg
= "Exception in main webserver thread";
646 const string acceptmsg
= "Exception while accepting a connection in main webserver thread";
648 auto client
= d_server
->accept();
652 if (client
->acl(d_acl
)) {
653 std::thread
webHandler(WebServerConnectionThreadStart
, this, client
);
657 if (client
->getRemote(remote
))
658 g_log
<<Logger::Error
<<d_logprefix
<<"Webserver closing socket: remote ("<< remote
.toString() <<") does not match the set ACL("<<d_acl
.toString()<<")"<<endl
;
661 catch(PDNSException
&e
) {
662 SLOG(g_log
<<Logger::Error
<<d_logprefix
<<"PDNSException while accepting a connection in main webserver thread: "<<e
.reason
<<endl
,
663 d_slog
->error(Logr::Error
, e
.reason
, acceptmsg
, Logging::Loggable("PDNSException")));
665 catch(std::exception
&e
) {
666 SLOG(g_log
<<Logger::Error
<<d_logprefix
<<"STL Exception while accepting a connection in main webserver thread: "<<e
.what()<<endl
,
667 d_slog
->error(Logr::Error
, e
.what(), acceptmsg
, Logging::Loggable("std::exception")));
670 SLOG(g_log
<<Logger::Error
<<d_logprefix
<<"Unknown exception while accepting a connection in main webserver thread"<<endl
,
671 d_slog
->info(Logr::Error
, msg
));
675 catch(PDNSException
&e
) {
676 SLOG(g_log
<<Logger::Error
<<d_logprefix
<<"PDNSException in main webserver thread: "<<e
.reason
<<endl
,
677 d_slog
->error(Logr::Error
, e
.reason
, msg
, Logging::Loggable("PDNSException")));
679 catch(std::exception
&e
) {
680 SLOG(g_log
<<Logger::Error
<<d_logprefix
<<"STL Exception in main webserver thread: "<<e
.what()<<endl
,
681 d_slog
->error(Logr::Error
, e
.what(), msg
, Logging::Loggable("std::exception")));
684 SLOG(g_log
<<Logger::Error
<<d_logprefix
<<"Unknown exception in main webserver thread"<<endl
,
685 d_slog
->info(Logr::Error
, msg
));