]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/webserver.cc
add OpenSSL exception to PowerDNS, Netherlabs, van Dijk and Hubert copyrights
[thirdparty/pdns.git] / pdns / webserver.cc
1 /*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002-2012 PowerDNS.COM BV
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2
7 as published by the Free Software Foundation
8
9 Additionally, the license of this program contains a special
10 exception which allows to distribute the program in binary form when
11 it is linked against OpenSSL.
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 St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22 #include "utility.hh"
23 #include "webserver.hh"
24 #include "session.hh"
25 #include "misc.hh"
26 #include <vector>
27 #include "logger.hh"
28 #include <stdio.h>
29 #include "dns.hh"
30 #include "base64.hh"
31 #include "json.hh"
32
33
34 map<string,WebServer::HandlerFunction *>WebServer::d_functions;
35 void *WebServer::d_that;
36 string WebServer::d_password;
37
38 int WebServer::B64Decode(const std::string& strInput, std::string& strOutput)
39 {
40 return ::B64Decode(strInput, strOutput);
41 }
42
43 void WebServer::registerHandler(const string&s, HandlerFunction *ptr)
44 {
45 d_functions[s]=ptr;
46 }
47
48 void WebServer::setCaller(void *that)
49 {
50 d_that=that;
51 }
52
53 void *WebServer::serveConnection(void *p)
54 try {
55 pthread_detach(pthread_self());
56 Session *client=static_cast<Session *>(p);
57 bool want_html=false;
58 bool want_json=false;
59
60 try {
61 string line;
62 client->setTimeout(5);
63 client->getLine(line);
64 stripLine(line);
65 if(line.empty())
66 throw HttpBadRequestException();
67 // L<<"page: "<<line<<endl;
68
69 vector<string> parts;
70 stringtok(parts,line);
71
72 string method, uri;
73 if(parts.size()>1) {
74 method=parts[0];
75 uri=parts[1];
76 }
77
78 vector<string>variables;
79
80 parts.clear();
81 stringtok(parts,uri,"?");
82
83 // L<<"baseUrl: '"<<parts[0]<<"'"<<endl;
84
85 vector<string>urlParts;
86 stringtok(urlParts,parts[0],"/");
87 string baseUrl;
88 if(urlParts.empty())
89 baseUrl="";
90 else
91 baseUrl=urlParts[0];
92
93 // L<<"baseUrl real: '"<<baseUrl<<"'"<<endl;
94
95 if(parts.size()>1) {
96 stringtok(variables,parts[1],"&");
97 }
98
99 map<string,string>varmap;
100
101 for(vector<string>::const_iterator i=variables.begin();
102 i!=variables.end();++i) {
103
104 parts.clear();
105 stringtok(parts,*i,"=");
106 if(parts.size()>1)
107 varmap[parts[0]]=parts[1];
108 else
109 varmap[parts[0]]="";
110
111 }
112
113 bool authOK=0;
114 int postlen = 0;
115 // read & ignore other lines
116 do {
117 client->getLine(line);
118 stripLine(line);
119
120 if(line.empty())
121 break;
122
123 size_t colon = line.find(":");
124 if(colon==std::string::npos)
125 throw HttpBadRequestException();
126
127 string header = toLower(line.substr(0, colon));
128 string value = line.substr(line.find_first_not_of(' ', colon+1));
129
130 if(header == "authorization" && toLower(value).find("basic ") == 0) {
131 string cookie=value.substr(6);
132 string plain;
133
134 B64Decode(cookie,plain);
135 vector<string>cparts;
136 stringtok(cparts,plain,":");
137 // L<<Logger::Error<<"Entered password: '"<<cparts[1].c_str()<<"', should be '"<<d_password.c_str()<<"'"<<endl;
138 if(cparts.size()==2 && !strcmp(cparts[1].c_str(),d_password.c_str())) { // this gets rid of terminating zeros
139 authOK=1;
140 }
141 }
142 else if(header == "content-length" && method=="POST") {
143 postlen = atoi(value.c_str());
144 // cout<<"Got a post: "<<postlen<<" bytes"<<endl;
145 }
146 else if(header == "accept") {
147 // json wins over html
148 if(value.find("application/json")!=std::string::npos) {
149 want_json=true;
150 } else if(value.find("text/html")!=std::string::npos) {
151 want_html=true;
152 }
153 }
154 else
155 ; // cerr<<"Ignoring line: "<<line<<endl;
156
157 } while(true);
158
159 string post;
160 if(postlen)
161 post = client->get(postlen);
162
163 // cout<<"Post: '"<<post<<"'"<<endl;
164
165 if(!d_password.empty() && !authOK)
166 throw HttpUnauthorizedException();
167
168 HandlerFunction *fptr;
169 if(d_functions.count(baseUrl) && (fptr=d_functions[baseUrl])) {
170 bool custom=false;
171 string ret=(*fptr)(method, post, varmap, d_that, &custom);
172
173 if(!custom) {
174 client->putLine("HTTP/1.1 200 OK\n");
175 client->putLine("Connection: close\n");
176 client->putLine("Content-Type: text/html; charset=utf-8\n\n");
177 }
178 client->putLine(ret);
179 }
180 else {
181 throw HttpNotFoundException();
182 }
183
184 }
185 catch(HttpException &e) {
186 client->putLine(e.statusLine());
187 client->putLine("Connection: close\n");
188 client->putLine(e.headers());
189 if(want_html) {
190 client->putLine("Content-Type: text/html; charset=utf-8\n\n");
191 client->putLine("<!html><title>" + e.what() + "</title><h1>" + e.what() + "</h1>");
192 } else if (want_json) {
193 client->putLine("Content-Type: application/json\n\n");
194 client->putLine(returnJSONError(e.what()));
195 } else {
196 client->putLine("Content-Type: text/plain; charset=utf-8\n\n");
197 client->putLine(e.what());
198 }
199 }
200
201 client->close();
202 delete client;
203 client=0;
204
205 return 0;
206 }
207 catch(SessionTimeoutException &e) {
208 // L<<Logger::Error<<"Timeout in webserver"<<endl;
209 }
210 catch(PDNSException &e) {
211 L<<Logger::Error<<"Exception in webserver: "<<e.reason<<endl;
212 }
213 catch(std::exception &e) {
214 L<<Logger::Error<<"STL Exception in webserver: "<<e.what()<<endl;
215 }
216 catch(...) {
217 L<<Logger::Error<<"Unknown exception in webserver"<<endl;
218 }
219
220
221 WebServer::WebServer(const string &listenaddress, int port, const string &password)
222 {
223 d_listenaddress=listenaddress;
224 d_port=port;
225 d_password=password;
226 d_server = 0; // on exception, this class becomes a NOOP later on
227 try {
228 d_server = new Server(d_port, d_listenaddress);
229 }
230 catch(SessionException &e) {
231 L<<Logger::Error<<"Fatal error in webserver: "<<e.reason<<endl;
232 }
233 }
234
235 void WebServer::go()
236 {
237 if(!d_server)
238 return;
239 try {
240 Session *client;
241 pthread_t tid;
242
243 L<<Logger::Error<<"Launched webserver on " << d_server->d_local.toStringWithPort() <<endl;
244
245 while((client=d_server->accept())) {
246 pthread_create(&tid, 0 , &serveConnection, (void *)client);
247 }
248 }
249 catch(SessionTimeoutException &e) {
250 // L<<Logger::Error<<"Timeout in webserver"<<endl;
251 }
252 catch(PDNSException &e) {
253 L<<Logger::Error<<"Exception in main webserver thread: "<<e.reason<<endl;
254 }
255 catch(std::exception &e) {
256 L<<Logger::Error<<"STL Exception in main webserver thread: "<<e.what()<<endl;
257 }
258 catch(...) {
259 L<<Logger::Error<<"Unknown exception in main webserver thread"<<endl;
260 }
261 exit(1);
262
263 }