]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/webserver.cc
Merge pull request #14020 from omoerbeek/rec-compiling-rust-dcos
[thirdparty/pdns.git] / pdns / webserver.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
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.
8 *
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.
12 *
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.
17 *
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.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "utility.hh"
26 #include "webserver.hh"
27 #include "misc.hh"
28 #include <thread>
29 #include "threadname.hh"
30 #include <utility>
31 #include <vector>
32 #include "logger.hh"
33 #include <stdio.h>
34 #include "dns.hh"
35 #include "base64.hh"
36 #include "json.hh"
37 #include "uuid-utils.hh"
38 #include <yahttp/router.hpp>
39 #include <algorithm>
40 #include <unordered_set>
41
42 json11::Json HttpRequest::json()
43 {
44 string err;
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();
49 }
50 json11::Json doc = json11::Json::parse(this->body, err);
51 if (doc.is_null()) {
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();
55 }
56 return doc;
57 }
58
59 bool HttpRequest::compareAuthorization(const CredentialsHolder& credentials) const
60 {
61 // validate password
62 auto header = headers.find("authorization");
63 bool auth_ok = false;
64 if (header != headers.end() && toLower(header->second).find("basic ") == 0) {
65 string cookie = header->second.substr(6);
66
67 string plain;
68 B64Decode(cookie, plain);
69
70 vector<string> cparts;
71 stringtok(cparts, plain, ":");
72
73 auth_ok = (cparts.size() == 2 && credentials.matches(cparts[1].c_str()));
74 }
75 return auth_ok;
76 }
77
78 bool HttpRequest::compareHeader(const string &header_name, const string &expected_value) const
79 {
80 auto header = headers.find(header_name);
81 if (header == headers.end()) {
82 return false;
83 }
84
85 // this gets rid of terminating zeros
86 return (0==strcmp(header->second.c_str(), expected_value.c_str()));
87 }
88
89 bool HttpRequest::compareHeader(const string &header_name, const CredentialsHolder& credentials) const
90 {
91 auto header = headers.find(header_name);
92 if (header == headers.end()) {
93 return false;
94 }
95
96 return credentials.matches(header->second);
97 }
98
99 void HttpResponse::setPlainBody(const string& document)
100 {
101 this->headers["Content-Type"] = "text/plain; charset=utf-8";
102
103 this->body = document;
104 }
105
106 void HttpResponse::setYamlBody(const string& document)
107 {
108 this->headers["Content-Type"] = "application/x-yaml";
109
110 this->body = document;
111 }
112
113 void HttpResponse::setJsonBody(const string& document)
114 {
115 this->headers["Content-Type"] = "application/json";
116
117 this->body = document;
118 }
119
120 void HttpResponse::setJsonBody(const json11::Json& document)
121 {
122 this->headers["Content-Type"] = "application/json";
123
124 document.dump(this->body);
125 }
126
127 void HttpResponse::setErrorResult(const std::string& message, const int status_)
128 {
129 setJsonBody(json11::Json::object { { "error", message } });
130 this->status = status_;
131 }
132
133 void HttpResponse::setSuccessResult(const std::string& message, const int status_)
134 {
135 setJsonBody(json11::Json::object { { "result", message } });
136 this->status = status_;
137 }
138
139 static void bareHandlerWrapper(const WebServer::HandlerFunction& handler, YaHTTP::Request* req, YaHTTP::Response* resp)
140 {
141 // wrapper to convert from YaHTTP::* to our subclasses
142 handler(static_cast<HttpRequest*>(req), static_cast<HttpResponse*>(resp));
143 }
144
145 void WebServer::registerBareHandler(const string& url, const HandlerFunction& handler, const std::string& method)
146 {
147 YaHTTP::THandlerFunction f = [=](YaHTTP::Request* req, YaHTTP::Response* resp){return bareHandlerWrapper(handler, req, resp);};
148 YaHTTP::Router::Map(method, url, std::move(f));
149 }
150
151 void WebServer::apiWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp, bool allowPassword) {
152 resp->headers["access-control-allow-origin"] = "*";
153
154 if (!d_apikey) {
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");
158 }
159
160 bool auth_ok = req->compareHeader("x-api-key", *d_apikey) || d_apikey->matches(req->getvars["api-key"]);
161
162 if (!auth_ok && allowPassword) {
163 if (d_webserverPassword) {
164 auth_ok = req->compareAuthorization(*d_webserverPassword);
165 } else {
166 auth_ok = true;
167 }
168 }
169
170 if (!auth_ok) {
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");
174 }
175
176 // security headers
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'";
182
183 req->getvars.erase("_"); // jQuery cache buster
184
185 try {
186 resp->status = 200;
187 handler(req, resp);
188 } catch (ApiException &e) {
189 resp->setErrorResult(e.what(), 422);
190 return;
191 } catch (JsonException &e) {
192 resp->setErrorResult(e.what(), 422);
193 return;
194 }
195
196 if (resp->status == 204) {
197 // No Content -> no Content-Type.
198 resp->headers.erase("Content-Type");
199 }
200 }
201
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);
205 }
206
207 void WebServer::webWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp) {
208 if (d_webserverPassword) {
209 bool auth_ok = req->compareAuthorization(*d_webserverPassword);
210 if (!auth_ok) {
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");
214 }
215 }
216
217 handler(req, resp);
218 }
219
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);
223 }
224
225 static void* WebServerConnectionThreadStart(const WebServer* webServer, const std::shared_ptr<Socket>& client)
226 {
227 setThreadName("rec/webhndlr");
228 const std::string msg = "Exception while serving a connection in main webserver thread";
229 try {
230 webServer->serveConnection(client);
231 }
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")));
235 }
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")));
239 }
240 catch(...) {
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));
243 }
244 return nullptr;
245 }
246
247 void WebServer::handleRequest(HttpRequest& req, HttpResponse& resp) const
248 {
249 // set default headers
250 resp.headers["Content-Type"] = "text/html; charset=utf-8";
251
252 #ifdef RECURSOR
253 auto log = req.d_slog->withValues("urlpath", Logging::Loggable(req.url.path));
254 #endif
255
256 try {
257 if (!req.complete) {
258 SLOG(g_log<<Logger::Debug<<req.logprefix<<"Incomplete request" << endl,
259 d_slog->info(Logr::Debug, "Incomplete request"));
260 throw HttpBadRequestException();
261 }
262 SLOG(g_log<<Logger::Debug<<req.logprefix<<"Handling request \"" << req.url.path << "\"" << endl,
263 log->info(Logr::Debug, "Handling request"));
264
265 YaHTTP::strstr_map_t::iterator header;
266
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;
277 }
278 }
279
280 YaHTTP::THandlerFunction handler;
281 YaHTTP::RoutingResult res = YaHTTP::Router::Route(&req, handler);
282
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();
287 }
288 if (res == YaHTTP::RouteNoMethod) {
289 throw HttpMethodNotAllowedException();
290 }
291
292 const string msg = "HTTP ISE Exception";
293 try {
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())));
297 }
298 catch(HttpException&) {
299 throw;
300 }
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();
305 }
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();
310 }
311 catch(...) {
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();
315 }
316 }
317 catch(HttpException &e) {
318 resp = e.response();
319 #ifdef RECURSOR
320 // An HttpException does not initialize d_slog
321 if (!resp.d_slog) {
322 resp.setSLog(log);
323 }
324 #endif
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);
333 }
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>";
337 } else {
338 resp.headers["Content-Type"] = "text/plain; charset=utf-8";
339 resp.body = std::move(what);
340 }
341 }
342
343 // always set these headers
344 resp.headers["Connection"] = "close";
345
346 if (req.method == "HEAD") {
347 resp.body = "";
348 } else {
349 resp.headers["Content-Length"] = std::to_string(resp.body.size());
350 }
351 }
352
353 #ifdef RECURSOR
354 // Helper to log key-value maps used by YaHTTP
355 template<>
356 std::string Logging::IterLoggable<YaHTTP::strstr_map_t::const_iterator>::to_string() const
357 {
358 std::ostringstream oss;
359 bool first = true;
360 for (auto i = _t1; i != _t2; i++) {
361 if (!first) {
362 oss << '\n';
363 }
364 else {
365 first = false;
366 }
367 oss << i->first << ": " << i->second;
368 }
369 return oss.str();
370 }
371 #endif
372
373 void WebServer::logRequest(const HttpRequest& req, [[maybe_unused]] const ComboAddress& remote) const {
374 if (d_loglevel >= WebServer::LogLevel::Detailed) {
375 #ifdef RECURSOR
376 if (!g_slogStructured) {
377 #endif
378 const auto& logprefix = req.logprefix;
379 g_log<<Logger::Notice<<logprefix<<"Request details:"<<endl;
380
381 bool first = true;
382 for (const auto& r : req.getvars) {
383 if (first) {
384 first = false;
385 g_log<<Logger::Notice<<logprefix<<" GET params:"<<endl;
386 }
387 g_log<<Logger::Notice<<logprefix<<" "<<r.first<<": "<<r.second<<endl;
388 }
389
390 first = true;
391 for (const auto& r : req.postvars) {
392 if (first) {
393 first = false;
394 g_log<<Logger::Notice<<logprefix<<" POST params:"<<endl;
395 }
396 g_log<<Logger::Notice<<logprefix<<" "<<r.first<<": "<<r.second<<endl;
397 }
398
399 first = true;
400 for (const auto& h : req.headers) {
401 if (first) {
402 first = false;
403 g_log<<Logger::Notice<<logprefix<<" Headers:"<<endl;
404 }
405 g_log<<Logger::Notice<<logprefix<<" "<<h.first<<": "<<h.second<<endl;
406 }
407
408 if (req.body.empty()) {
409 g_log<<Logger::Notice<<logprefix<<" No body"<<endl;
410 } else {
411 g_log<<Logger::Notice<<logprefix<<" Full body: "<<endl;
412 g_log<<Logger::Notice<<logprefix<<" "<<req.body<<endl;
413 }
414 #ifdef RECURSOR
415 }
416 else {
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));
421 }
422 #endif
423 }
424 }
425
426 void WebServer::logResponse(const HttpResponse& resp, const ComboAddress& /* remote */, const string& logprefix) const {
427 if (d_loglevel >= WebServer::LogLevel::Detailed) {
428 #ifdef RECURSOR
429 if (!g_slogStructured) {
430 #endif
431 g_log<<Logger::Notice<<logprefix<<"Response details:"<<endl;
432 bool first = true;
433 for (const auto& h : resp.headers) {
434 if (first) {
435 first = false;
436 g_log<<Logger::Notice<<logprefix<<" Headers:"<<endl;
437 }
438 g_log<<Logger::Notice<<logprefix<<" "<<h.first<<": "<<h.second<<endl;
439 }
440 if (resp.body.empty()) {
441 g_log<<Logger::Notice<<logprefix<<" No body"<<endl;
442 } else {
443 g_log<<Logger::Notice<<logprefix<<" Full body: "<<endl;
444 g_log<<Logger::Notice<<logprefix<<" "<<resp.body<<endl;
445 }
446 #ifdef RECURSOR
447 }
448 else {
449 resp.d_slog->info(Logr::Info, "Response details", "headers", Logging::IterLoggable(resp.headers.cbegin(), resp.headers.cend()),
450 "body", Logging::Loggable(resp.body));
451 }
452 #endif
453 }
454 }
455
456
457 struct ValidChars {
458 ValidChars()
459 {
460 // letter may be signed, but we only pass positive values
461 for (auto letter : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=") {
462 set.set(letter);
463 }
464 }
465 std::bitset<127> set;
466 };
467
468 static const ValidChars validChars;
469
470 static bool validURLChars(const string& str)
471 {
472 for (auto iter = str.begin(); iter != str.end(); ++iter) {
473 if (*iter == '%') {
474 ++iter;
475 if (iter == str.end() || isxdigit(static_cast<unsigned char>(*iter)) == 0) {
476 return false;
477 }
478 ++iter;
479 if (iter == str.end() || isxdigit(static_cast<unsigned char>(*iter)) == 0) {
480 return false;
481 }
482 }
483 else if (static_cast<size_t>(*iter) >= validChars.set.size() || !validChars.set[*iter]) {
484 return false;
485 }
486 }
487 return true;
488 }
489
490 bool WebServer::validURL(const YaHTTP::URL& url)
491 {
492 bool isOK = true;
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);
500 return isOK;
501 }
502
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) + " ";
506
507 HttpRequest req(logprefix);
508
509 HttpResponse resp;
510 #ifdef RECURSOR
511 auto log = d_slog->withValues("uniqueid", Logging::Loggable(to_string(unique)));
512 req.setSLog(log);
513 resp.setSLog(log);
514 #endif
515 resp.max_response_size=d_maxbodysize;
516 ComboAddress remote;
517 string reply;
518
519 try {
520 YaHTTP::AsyncRequestLoader yarl;
521 yarl.initialize(&req);
522 req.max_request_size=d_maxbodysize;
523 int timeout = 5;
524 client->setNonBlocking();
525
526 try {
527 while(!req.complete) {
528 int bytes;
529 char buf[16000];
530 bytes = client->readWithTimeout(buf, sizeof(buf), timeout);
531 if (bytes > 0) {
532 string data = string(buf, bytes);
533 req.complete = yarl.feed(data);
534 } else {
535 // read error OR EOF
536 break;
537 }
538 }
539 yarl.finalize();
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"));
544 }
545
546 if (!validURL(req.url)) {
547 throw PDNSException("Received request with invalid URL");
548 }
549 // Uses of `remote` below guarded by d_loglevel
550 if (d_loglevel > WebServer::LogLevel::None) {
551 client->getRemote(remote);
552 }
553
554 logRequest(req, remote);
555
556 WebServer::handleRequest(req, resp);
557 ostringstream ss;
558 resp.write(ss);
559 reply = ss.str();
560
561 logResponse(resp, remote, logprefix);
562
563 client->writenWithTimeout(reply.c_str(), reply.size(), timeout);
564 }
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")));
568 }
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")));
573 }
574 catch(...) {
575 SLOG(g_log<<Logger::Error<<logprefix<<"Unknown exception"<<endl,
576 d_slog->info(Logr::Error, "HTTP Exception"));
577 }
578
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())));
584 }
585 }
586
587 WebServer::WebServer(string listenaddress, int port) :
588 d_listenaddress(std::move(listenaddress)),
589 d_port(port),
590 d_server(nullptr),
591 d_maxbodysize(2*1024*1024)
592 {
593 YaHTTP::Router::Map("OPTIONS", "/<*url>", [](YaHTTP::Request *req, YaHTTP::Response *resp) {
594 // look for url in routes
595 bool seen = false;
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") {
601 continue;
602 }
603 std::map<std::string, YaHTTP::TDelim> params;
604 if (YaHTTP::Router::Match(url, req->url, params)) {
605 methods.push_back(method);
606 seen = true;
607 }
608 }
609 if (!seen) {
610 resp->status = 404;
611 resp->body = "";
612 return;
613 }
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";
619 resp->status = 200;
620 resp->headers["content-type"]= "text/plain";
621 resp->body = "";
622 }, "OptionsHandlerRoute");
623 }
624
625 void WebServer::bind()
626 {
627 try {
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)));
631 }
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")));
635 d_server = nullptr;
636 }
637 }
638
639 void WebServer::go()
640 {
641 if(!d_server)
642 return;
643 const string msg = "Exception in main webserver thread";
644 try {
645 while(true) {
646 const string acceptmsg = "Exception while accepting a connection in main webserver thread";
647 try {
648 auto client = d_server->accept();
649 if (!client) {
650 continue;
651 }
652 if (client->acl(d_acl)) {
653 std::thread webHandler(WebServerConnectionThreadStart, this, client);
654 webHandler.detach();
655 } else {
656 ComboAddress remote;
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;
659 }
660 }
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")));
664 }
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")));
668 }
669 catch(...) {
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));
672 }
673 }
674 }
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")));
678 }
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")));
682 }
683 catch(...) {
684 SLOG(g_log<<Logger::Error<<d_logprefix<<"Unknown exception in main webserver thread"<<endl,
685 d_slog->info(Logr::Error, msg));
686 }
687 _exit(1);
688 }