]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/webserver.hh
Merge pull request #14020 from omoerbeek/rec-compiling-rust-dcos
[thirdparty/pdns.git] / pdns / webserver.hh
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 #pragma once
23 #include <map>
24 #include <string>
25 #include <list>
26 #include <boost/utility.hpp>
27 #pragma GCC diagnostic push
28 #pragma GCC diagnostic ignored "-Woverloaded-virtual"
29 #include <yahttp/yahttp.hpp>
30 #pragma GCC diagnostic pop
31
32 #include "json11.hpp"
33
34 #include "credentials.hh"
35 #include "namespaces.hh"
36 #include "sstuff.hh"
37 #include "logging.hh"
38
39 class HttpRequest : public YaHTTP::Request {
40 public:
41 HttpRequest(const string& logprefix_="") : YaHTTP::Request(), logprefix(logprefix_) { };
42
43 string logprefix;
44 bool accept_yaml{false};
45 bool accept_json{false};
46 bool accept_html{false};
47 bool complete{false};
48
49 json11::Json json();
50
51 // checks password _only_.
52 bool compareAuthorization(const CredentialsHolder& expectedCredentials) const;
53 bool compareHeader(const string &header_name, const CredentialsHolder& expectedCredentials) const;
54 bool compareHeader(const string &header_name, const string &expected_value) const;
55
56 #ifdef RECURSOR
57 void setSLog(Logr::log_t log)
58 {
59 d_slog = log;
60 }
61 std::shared_ptr<Logr::Logger> d_slog;
62 #endif
63 };
64
65 class HttpResponse: public YaHTTP::Response {
66 public:
67 HttpResponse() : YaHTTP::Response() { };
68 HttpResponse(const YaHTTP::Response &resp) : YaHTTP::Response(resp) { };
69
70 void setPlainBody(const string& document);
71 void setYamlBody(const string& document);
72 void setJsonBody(const string& document);
73 void setJsonBody(const json11::Json& document);
74 void setErrorResult(const std::string& message, const int status);
75 void setSuccessResult(const std::string& message, const int status = 200);
76
77 #ifdef RECURSOR
78 void setSLog(Logr::log_t log)
79 {
80 d_slog = log;
81 }
82 std::shared_ptr<Logr::Logger> d_slog;
83 #endif
84 };
85
86
87 class HttpException
88 {
89 public:
90 HttpException(int status) : d_response()
91 {
92 d_response.status = status;
93 };
94
95 HttpException(int status, const string& msg) : d_response()
96 {
97 d_response.setErrorResult(msg, status);
98 };
99
100 HttpResponse response()
101 {
102 return d_response;
103 }
104
105 protected:
106 HttpResponse d_response;
107 };
108
109 class HttpBadRequestException : public HttpException {
110 public:
111 HttpBadRequestException() : HttpException(400) { };
112 HttpBadRequestException(const string& msg) : HttpException(400, msg) { };
113 };
114
115 class HttpUnauthorizedException : public HttpException {
116 public:
117 HttpUnauthorizedException(string const &scheme) : HttpException(401)
118 {
119 d_response.headers["WWW-Authenticate"] = scheme + " realm=\"PowerDNS\"";
120 }
121 };
122
123 class HttpForbiddenException : public HttpException {
124 public:
125 HttpForbiddenException() : HttpException(403) { };
126 HttpForbiddenException(const string& msg) : HttpException(403, msg) { };
127 };
128
129 class HttpNotFoundException : public HttpException {
130 public:
131 HttpNotFoundException() : HttpException(404) { };
132 HttpNotFoundException(const string& msg) : HttpException(404, msg) { };
133 };
134
135 class HttpMethodNotAllowedException : public HttpException {
136 public:
137 HttpMethodNotAllowedException() : HttpException(405) { };
138 HttpMethodNotAllowedException(const string& msg) : HttpException(405, msg) { };
139 };
140
141 class HttpConflictException : public HttpException {
142 public:
143 HttpConflictException() : HttpException(409) { };
144 HttpConflictException(const string& msg) : HttpException(409, msg) { };
145 };
146
147 class HttpInternalServerErrorException : public HttpException {
148 public:
149 HttpInternalServerErrorException() : HttpException(500) { };
150 HttpInternalServerErrorException(const string& msg) : HttpException(500, msg) { };
151 };
152
153 class ApiException : public runtime_error
154 {
155 public:
156 ApiException(const string& what_arg) : runtime_error(what_arg) {
157 }
158 };
159
160 class Server
161 {
162 public:
163 Server(const string &localaddress, int port) : d_local(localaddress.empty() ? "0.0.0.0" : localaddress, port), d_server_socket(d_local.sin4.sin_family, SOCK_STREAM, 0) {
164 d_server_socket.setReuseAddr();
165 d_server_socket.bind(d_local);
166 d_server_socket.listen();
167 }
168 virtual ~Server() = default;
169
170 ComboAddress d_local;
171
172 std::shared_ptr<Socket> accept() {
173 return std::shared_ptr<Socket>(d_server_socket.accept());
174 }
175
176 protected:
177 Socket d_server_socket;
178 };
179
180 class WebServer : public boost::noncopyable
181 {
182 public:
183 WebServer(string listenaddress, int port);
184 virtual ~WebServer() = default;
185
186 #ifdef RECURSOR
187 void setSLog(Logr::log_t log)
188 {
189 d_slog = log;
190 }
191 #endif
192
193 void setApiKey(const string &apikey, bool hashPlaintext) {
194 if (!apikey.empty()) {
195 d_apikey = make_unique<CredentialsHolder>(std::string(apikey), hashPlaintext);
196 }
197 else {
198 d_apikey.reset();
199 }
200 }
201
202 void setPassword(const string &password, bool hashPlaintext) {
203 if (!password.empty()) {
204 d_webserverPassword = make_unique<CredentialsHolder>(std::string(password), hashPlaintext);
205 }
206 else {
207 d_webserverPassword.reset();
208 }
209 }
210
211 void setMaxBodySize(ssize_t s) { // in megabytes
212 d_maxbodysize = s * 1024 * 1024;
213 }
214
215 void setACL(const NetmaskGroup &nmg) {
216 d_acl = nmg;
217 }
218
219 static bool validURL(const YaHTTP::URL& url);
220
221 void bind();
222 void go();
223
224 void serveConnection(const std::shared_ptr<Socket>& client) const;
225 void handleRequest(HttpRequest& request, HttpResponse& resp) const;
226
227 typedef std::function<void(HttpRequest* req, HttpResponse* resp)> HandlerFunction;
228 void registerApiHandler(const string& url, const HandlerFunction& handler, const std::string& method = "", bool allowPassword=false);
229 void registerWebHandler(const string& url, const HandlerFunction& handler, const std::string& method = "");
230
231 enum class LogLevel : uint8_t {
232 None = 0, // No logs from requests at all
233 Normal = 10, // A "common log format"-like line e.g. '127.0.0.1 "GET /apache_pb.gif HTTP/1.0" 200 2326'
234 Detailed = 20, // The full request headers and body, and the full response headers and body
235 };
236
237 void setLogLevel(const string& level) {
238 if (level == "none") {
239 d_loglevel = LogLevel::None;
240 return;
241 }
242
243 if (level == "normal") {
244 d_loglevel = LogLevel::Normal;
245 return;
246 }
247
248 if (level == "detailed") {
249 d_loglevel = LogLevel::Detailed;
250 return;
251 }
252
253 throw PDNSException("Unknown webserver log level: " + level);
254 }
255
256 void setLogLevel(const LogLevel level) {
257 d_loglevel = level;
258 };
259
260 LogLevel getLogLevel() {
261 return d_loglevel;
262 };
263
264 #ifdef RECURSOR
265 std::shared_ptr<Logr::Logger> d_slog;
266 #endif
267
268 protected:
269 static void registerBareHandler(const string& url, const HandlerFunction& handler, const std::string& method);
270 void logRequest(const HttpRequest& req, const ComboAddress& remote) const;
271 void logResponse(const HttpResponse& resp, const ComboAddress& remote, const string& logprefix) const;
272
273 virtual std::shared_ptr<Server> createServer() {
274 return std::make_shared<Server>(d_listenaddress, d_port);
275 }
276
277 void apiWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp, bool allowPassword);
278 void webWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp);
279
280 string d_listenaddress;
281 int d_port;
282 std::shared_ptr<Server> d_server;
283
284 std::unique_ptr<CredentialsHolder> d_apikey{nullptr};
285 std::unique_ptr<CredentialsHolder> d_webserverPassword{nullptr};
286
287 ssize_t d_maxbodysize; // in bytes
288
289 NetmaskGroup d_acl;
290
291 const string d_logprefix = "[webserver] ";
292
293 // Describes the amount of logging the webserver does
294 WebServer::LogLevel d_loglevel{WebServer::LogLevel::Detailed};
295 };