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