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