]>
Commit | Line | Data |
---|---|---|
12c86877 | 1 | /* |
12471842 PL |
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 | */ | |
e8c59f2d | 22 | #pragma once |
12c86877 BH |
23 | #include <map> |
24 | #include <string> | |
232f0877 | 25 | #include <list> |
3ae143b0 CH |
26 | #include <boost/utility.hpp> |
27 | #include <yahttp/yahttp.hpp> | |
5938c49f | 28 | #include "json11.hpp" |
10f4eea8 | 29 | #include "namespaces.hh" |
825fa717 | 30 | #include "sstuff.hh" |
12c86877 | 31 | |
80d59cd1 CH |
32 | class HttpRequest : public YaHTTP::Request { |
33 | public: | |
e35f9e46 | 34 | HttpRequest(const string& logprefix_="") : YaHTTP::Request(), accept_json(false), accept_html(false), complete(false), logprefix(logprefix_) { }; |
80d59cd1 | 35 | |
80d59cd1 CH |
36 | bool accept_json; |
37 | bool accept_html; | |
825fa717 | 38 | bool complete; |
a53cd863 | 39 | string logprefix; |
5938c49f | 40 | json11::Json json(); |
bbef8f04 CH |
41 | |
42 | // checks password _only_. | |
43 | bool compareAuthorization(const string &expected_password); | |
44 | bool compareHeader(const string &header_name, const string &expected_value); | |
80d59cd1 CH |
45 | }; |
46 | ||
47 | class HttpResponse: public YaHTTP::Response { | |
48 | public: | |
49 | HttpResponse() : YaHTTP::Response() { }; | |
80d59cd1 | 50 | HttpResponse(const YaHTTP::Response &resp) : YaHTTP::Response(resp) { }; |
669822d0 | 51 | |
5938c49f | 52 | void setBody(const json11::Json& document); |
692829aa CH |
53 | void setErrorResult(const std::string& message, const int status); |
54 | void setSuccessResult(const std::string& message, const int status = 200); | |
80d59cd1 CH |
55 | }; |
56 | ||
57 | ||
33196945 CH |
58 | class HttpException |
59 | { | |
60 | public: | |
80d59cd1 | 61 | HttpException(int status) : d_response() |
33196945 | 62 | { |
80d59cd1 | 63 | d_response.status = status; |
33196945 CH |
64 | }; |
65 | ||
8204102e PL |
66 | HttpException(int status, const string& msg) : d_response() |
67 | { | |
68 | d_response.setErrorResult(msg, status); | |
69 | }; | |
70 | ||
80d59cd1 CH |
71 | HttpResponse response() |
72 | { | |
73 | return d_response; | |
33196945 CH |
74 | } |
75 | ||
80d59cd1 CH |
76 | protected: |
77 | HttpResponse d_response; | |
33196945 CH |
78 | }; |
79 | ||
80 | class HttpBadRequestException : public HttpException { | |
81 | public: | |
80d59cd1 | 82 | HttpBadRequestException() : HttpException(400) { }; |
8204102e | 83 | HttpBadRequestException(const string& msg) : HttpException(400, msg) { }; |
33196945 CH |
84 | }; |
85 | ||
86 | class HttpUnauthorizedException : public HttpException { | |
87 | public: | |
53255086 | 88 | HttpUnauthorizedException(string const &scheme) : HttpException(401) |
80d59cd1 | 89 | { |
53255086 | 90 | d_response.headers["WWW-Authenticate"] = scheme + " realm=\"PowerDNS\""; |
33196945 CH |
91 | } |
92 | }; | |
93 | ||
53255086 PL |
94 | class HttpForbiddenException : public HttpException { |
95 | public: | |
96 | HttpForbiddenException() : HttpException(403) { }; | |
8204102e | 97 | HttpForbiddenException(const string& msg) : HttpException(403, msg) { }; |
53255086 PL |
98 | }; |
99 | ||
33196945 CH |
100 | class HttpNotFoundException : public HttpException { |
101 | public: | |
80d59cd1 | 102 | HttpNotFoundException() : HttpException(404) { }; |
8204102e | 103 | HttpNotFoundException(const string& msg) : HttpException(404, msg) { }; |
33196945 CH |
104 | }; |
105 | ||
106 | class HttpMethodNotAllowedException : public HttpException { | |
107 | public: | |
80d59cd1 | 108 | HttpMethodNotAllowedException() : HttpException(405) { }; |
8204102e | 109 | HttpMethodNotAllowedException(const string& msg) : HttpException(405, msg) { }; |
33196945 CH |
110 | }; |
111 | ||
331d3062 CH |
112 | class HttpConflictException : public HttpException { |
113 | public: | |
114 | HttpConflictException() : HttpException(409) { }; | |
f7b99555 | 115 | HttpConflictException(const string& msg) : HttpException(409, msg) { }; |
331d3062 CH |
116 | }; |
117 | ||
0f67eeda CH |
118 | class HttpInternalServerErrorException : public HttpException { |
119 | public: | |
120 | HttpInternalServerErrorException() : HttpException(500) { }; | |
8204102e | 121 | HttpInternalServerErrorException(const string& msg) : HttpException(500, msg) { }; |
0f67eeda CH |
122 | }; |
123 | ||
3ae143b0 CH |
124 | class ApiException : public runtime_error |
125 | { | |
126 | public: | |
1df079f5 | 127 | ApiException(const string& what_arg) : runtime_error(what_arg) { |
3ae143b0 CH |
128 | } |
129 | }; | |
02c04144 | 130 | |
a7650f23 CH |
131 | class Server |
132 | { | |
133 | public: | |
93f4e5ce | 134 | 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) { |
a7650f23 CH |
135 | d_server_socket.setReuseAddr(); |
136 | d_server_socket.bind(d_local); | |
137 | d_server_socket.listen(); | |
138 | } | |
690984d4 | 139 | virtual ~Server() { }; |
a7650f23 CH |
140 | |
141 | ComboAddress d_local; | |
142 | ||
d4c53d8c RG |
143 | std::shared_ptr<Socket> accept() { |
144 | return std::shared_ptr<Socket>(d_server_socket.accept()); | |
a7650f23 CH |
145 | } |
146 | ||
147 | protected: | |
148 | Socket d_server_socket; | |
149 | }; | |
150 | ||
3ae143b0 | 151 | class WebServer : public boost::noncopyable |
12c86877 BH |
152 | { |
153 | public: | |
bbef8f04 | 154 | WebServer(const string &listenaddress, int port); |
690984d4 | 155 | virtual ~WebServer() { }; |
29997a3c PL |
156 | |
157 | void setApiKey(const string &apikey) { | |
29997a3c PL |
158 | d_apikey = apikey; |
159 | } | |
160 | ||
0c3b088c | 161 | void setPassword(const string &password) { |
0c3b088c PL |
162 | d_webserverPassword = password; |
163 | } | |
164 | ||
214b034e PD |
165 | void setMaxBodySize(ssize_t s) { // in megabytes |
166 | d_maxbodysize = s * 1024 * 1024; | |
167 | } | |
168 | ||
0010aefa PL |
169 | void setACL(const NetmaskGroup &nmg) { |
170 | d_acl = nmg; | |
171 | } | |
172 | ||
825fa717 | 173 | void bind(); |
12c86877 | 174 | void go(); |
232f0877 | 175 | |
b184a9dc RG |
176 | void serveConnection(std::shared_ptr<Socket> client) const; |
177 | void handleRequest(HttpRequest& request, HttpResponse& resp) const; | |
232f0877 | 178 | |
80d59cd1 | 179 | typedef boost::function<void(HttpRequest* req, HttpResponse* resp)> HandlerFunction; |
c563cbe5 | 180 | void registerApiHandler(const string& url, HandlerFunction handler, bool allowPassword=false); |
bbef8f04 | 181 | void registerWebHandler(const string& url, HandlerFunction handler); |
232f0877 | 182 | |
a53cd863 PL |
183 | enum class LogLevel : uint8_t { |
184 | None = 0, // No logs from requests at all | |
a35306f9 | 185 | Normal = 10, // A "common log format"-like line e.g. '127.0.0.1 "GET /apache_pb.gif HTTP/1.0" 200 2326' |
a53cd863 PL |
186 | Detailed = 20, // The full request headers and body, and the full response headers and body |
187 | }; | |
188 | ||
60986c4b PL |
189 | void setLogLevel(const string& level) { |
190 | if (level == "none") { | |
191 | d_loglevel = LogLevel::None; | |
192 | return; | |
193 | } | |
194 | ||
a35306f9 PL |
195 | if (level == "normal") { |
196 | d_loglevel = LogLevel::Normal; | |
60986c4b PL |
197 | return; |
198 | } | |
199 | ||
200 | if (level == "detailed") { | |
201 | d_loglevel = LogLevel::Detailed; | |
202 | return; | |
203 | } | |
204 | ||
205 | throw PDNSException("Unknown webserver log level: " + level); | |
206 | } | |
207 | ||
a53cd863 PL |
208 | void setLogLevel(const LogLevel level) { |
209 | d_loglevel = level; | |
210 | }; | |
211 | ||
212 | LogLevel getLogLevel() { | |
213 | return d_loglevel; | |
214 | }; | |
b2cb8982 | 215 | |
3ae143b0 | 216 | protected: |
bbef8f04 | 217 | void registerBareHandler(const string& url, HandlerFunction handler); |
612ad9ec PL |
218 | void logRequest(const HttpRequest& req, const ComboAddress& remote) const; |
219 | void logResponse(const HttpResponse& resp, const ComboAddress& remote, const string& logprefix) const; | |
232f0877 | 220 | |
690984d4 RG |
221 | virtual std::shared_ptr<Server> createServer() { |
222 | return std::make_shared<Server>(d_listenaddress, d_port); | |
825fa717 CH |
223 | } |
224 | ||
12c86877 BH |
225 | string d_listenaddress; |
226 | int d_port; | |
232f0877 | 227 | string d_password; |
690984d4 | 228 | std::shared_ptr<Server> d_server; |
29997a3c PL |
229 | |
230 | std::string d_apikey; | |
c563cbe5 | 231 | void apiWrapper(WebServer::HandlerFunction handler, HttpRequest* req, HttpResponse* resp, bool allowPassword); |
0c3b088c | 232 | std::string d_webserverPassword; |
7579a7b9 | 233 | void webWrapper(WebServer::HandlerFunction handler, HttpRequest* req, HttpResponse* resp); |
0010aefa | 234 | |
214b034e PD |
235 | ssize_t d_maxbodysize; // in bytes |
236 | ||
0010aefa | 237 | NetmaskGroup d_acl; |
b2cb8982 PL |
238 | |
239 | const string d_logprefix = "[webserver] "; | |
a53cd863 PL |
240 | |
241 | // Describes the amount of logging the webserver does | |
242 | WebServer::LogLevel d_loglevel{WebServer::LogLevel::Detailed}; | |
12c86877 | 243 | }; |