]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/webserver.cc
Initial revision
[thirdparty/pdns.git] / pdns / webserver.cc
1 /*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002 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 as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 #include "utility.hh"
20 #include "webserver.hh"
21 #include "session.hh"
22 #include "misc.hh"
23 #include <vector>
24 #include "logger.hh"
25 #include <stdio.h>
26 #include "dns.hh"
27
28
29 map<string,WebServer::HandlerFunction *>WebServer::d_functions;
30 void *WebServer::d_that;
31 string WebServer::d_password;
32
33 char WebServer::B64Decode1(char cInChar)
34 {
35 // The incoming character will be A-Z, a-z, 0-9, +, /, or =.
36 // The idea is to quickly determine which grouping the
37 // letter belongs to and return the associated value
38 // without having to search the global encoding string
39 // (the value we're looking for would be the resulting
40 // index into that string).
41 //
42 // To do that, we'll play some tricks...
43 unsigned char iIndex = '\0';
44 switch ( cInChar ) {
45 case '+':
46 iIndex = 62;
47 break;
48
49 case '/':
50 iIndex = 63;
51 break;
52
53 case '=':
54 iIndex = 0;
55 break;
56
57 default:
58 // Must be 'A'-'Z', 'a'-'z', '0'-'9', or an error...
59 //
60 // Numerically, small letters are "greater" in value than
61 // capital letters and numerals (ASCII value), and capital
62 // letters are "greater" than numerals (again, ASCII value),
63 // so we check for numerals first, then capital letters,
64 // and finally small letters.
65 iIndex = '9' - cInChar;
66 if ( iIndex > 0x3F ) {
67 // Not from '0' to '9'...
68 iIndex = 'Z' - cInChar;
69 if ( iIndex > 0x3F ) {
70 // Not from 'A' to 'Z'...
71 iIndex = 'z' - cInChar;
72 if ( iIndex > 0x3F ) {
73 // Invalid character...cannot
74 // decode!
75 iIndex = 0x80; // set the high bit
76 } // if
77 else {
78 // From 'a' to 'z'
79 iIndex = (('z' - iIndex) - 'a') + 26;
80 } // else
81 } // if
82 else {
83 // From 'A' to 'Z'
84 iIndex = ('Z' - iIndex) - 'A';
85 } // else
86 } // if
87 else {
88 // Adjust the index...
89 iIndex = (('9' - iIndex) - '0') + 52;
90 } // else
91 break;
92
93 } // switch
94
95 return iIndex;
96 }
97
98 int WebServer::B64Decode(const std::string& strInput, std::string& strOutput)
99 {
100 // Set up a decoding buffer
101 long cBuf = 0;
102 char* pBuf = (char*)&cBuf;
103
104 // Decoding management...
105 short iBitGroup = 0, iInNum = 0;
106
107 // While there are characters to process...
108 //
109 // We'll decode characters in blocks of 4, as
110 // there are 4 groups of 6 bits in 3 bytes. The
111 // incoming Base64 character is first decoded, and
112 // then it is inserted into the decode buffer
113 // (with any relevant shifting, as required).
114 // Later, after all 3 bytes have been reconsituted,
115 // we assign them to the output string, ultimately
116 // to be returned as the original message.
117 int iInSize = strInput.size();
118 unsigned char cChar = '\0';
119 while ( iInNum < iInSize ) {
120 // Fill the decode buffer with 4 groups of 6 bits
121 cBuf = 0; // clear
122 for ( iBitGroup = 0; iBitGroup < 4; ++iBitGroup ) {
123 if ( iInNum < iInSize ) {
124 // Decode a character
125 cChar = B64Decode1(strInput.at(iInNum++));
126 } // if
127 else {
128 // Decode a padded zero
129 cChar = '\0';
130 } // else
131
132 // Check for valid decode
133 if ( cChar > 0x7F )
134 return -1;
135
136 // Adjust the bits
137 switch ( iBitGroup ) {
138 case 0:
139 // The first group is copied into
140 // the least significant 6 bits of
141 // the decode buffer...these 6 bits
142 // will eventually shift over to be
143 // the most significant bits of the
144 // third byte.
145 cBuf = cBuf | cChar;
146 break;
147
148 default:
149 // For groupings 1-3, simply shift
150 // the bits in the decode buffer over
151 // by 6 and insert the 6 from the
152 // current decode character.
153 cBuf = (cBuf << 6) | cChar;
154 break;
155
156 } // switch
157 } // for
158
159 // Interpret the resulting 3 bytes...note there
160 // may have been padding, so those padded bytes
161 // are actually ignored.
162 strOutput += pBuf[2];
163 strOutput += pBuf[1];
164 strOutput += pBuf[0];
165 } // while
166
167 return 1;
168 }
169
170
171
172
173 void WebServer::registerHandler(const string&s, HandlerFunction *ptr)
174 {
175 d_functions[s]=ptr;
176 }
177
178 void WebServer::setCaller(void *that)
179 {
180 d_that=that;
181 }
182
183 void *WebServer::serveConnection(void *p)
184 {
185 Session *client=static_cast<Session *>(p);
186 try {
187 string line;
188 client->getLine(line);
189 stripLine(line);
190 // L<<"page: "<<line<<endl;
191
192 vector<string> parts;
193 stringtok(parts,line);
194
195 string uri;
196 if(parts.size()>1)
197 uri=parts[1];
198
199 vector<string>variables;
200
201 parts.clear();
202 stringtok(parts,uri,"?");
203
204 // L<<"baseUrl: '"<<parts[0]<<"'"<<endl;
205
206 vector<string>urlParts;
207 stringtok(urlParts,parts[0],"/");
208 string baseUrl;
209 if(urlParts.empty())
210 baseUrl="";
211 else
212 baseUrl=urlParts[0];
213
214 // L<<"baseUrl real: '"<<baseUrl<<"'"<<endl;
215
216 if(parts.size()>1) {
217 stringtok(variables,parts[1],"&");
218 }
219
220 map<string,string>varmap;
221
222 for(vector<string>::const_iterator i=variables.begin();
223 i!=variables.end();++i) {
224
225 parts.clear();
226 stringtok(parts,*i,"=");
227 if(parts.size()>1)
228 varmap[parts[0]]=parts[1];
229 else
230 varmap[parts[0]]="";
231
232 L<<"'"<<parts[0]<<"' = '"<<varmap[parts[0]]<<"'"<<endl;
233 }
234
235 bool authOK=0;
236
237 // read & ignore other lines
238 do {
239 client->getLine(line);
240 stripLine(line);
241
242 if(!line.find("Authorization: Basic ")) {
243 string cookie=line.substr(21);
244 string plain;
245
246 B64Decode(cookie,plain);
247 vector<string>cparts;
248 stringtok(cparts,plain,":");
249
250 if(cparts.size()==2 && !strcmp(cparts[1].c_str(),d_password.c_str())) { // this gets rid of terminating zeros
251 authOK=1;
252 }
253 }
254 }while(!line.empty());
255
256
257 if(!d_password.empty() && !authOK) {
258 client->putLine("HTTP/1.1 401 OK\n");
259 client->putLine("WWW-Authenticate: Basic realm=\"PowerDNS\"\n");
260
261 client->putLine("Connection: close\n");
262 client->putLine("Content-type: text/html\n\n");
263 client->putLine("Please enter a valid password!\n");
264 client->close();
265 delete client;
266 return 0;
267 }
268
269 HandlerFunction *fptr;
270 if((fptr=d_functions[baseUrl])) {
271
272 bool custom;
273 string ret=(*fptr)(varmap, d_that, &custom);
274
275 if(!custom) {
276 client->putLine("HTTP/1.1 200 OK\n");
277 client->putLine("Connection: close\n");
278 client->putLine("Content-type: text/html\n\n");
279 }
280 client->putLine(ret);
281 }
282 else {
283 client->putLine("HTTP/1.1 404 Not found\n");
284 client->putLine("Connection: close\n");
285 client->putLine("Content-type: text/html\n\n");
286 // FIXME: CSS problem?
287 client->putLine("<html><body><h1>Did not find file '"+baseUrl+"'</body></html>\n");
288 }
289
290 client->close();
291 delete client;
292 client=0;
293 return 0;
294
295 }
296 catch(SessionTimeoutException &e) {
297 L<<Logger::Error<<"Timeout in webserver"<<endl;
298 }
299 catch(SessionException &e) {
300 L<<Logger::Error<<"Fatal error in webserver: "<<e.reason<<endl;
301 }
302 catch(Exception &e) {
303 L<<Logger::Error<<"Exception in webserver: "<<e.reason<<endl;
304 }
305 catch(exception &e) {
306 L<<Logger::Error<<"STL Exception in webserver: "<<e.what()<<endl;
307 }
308 catch(...) {
309 L<<Logger::Error<<"Unknown exception in webserver"<<endl;
310 }
311 if(client) {
312 client->close();
313 delete client;
314 client=0;
315 }
316 return 0;
317 }
318
319 WebServer::WebServer(const string &listenaddress, int port, const string &password)
320 {
321 d_listenaddress=listenaddress;
322 d_port=port;
323 d_password=password;
324 }
325
326 void WebServer::go()
327 {
328 try {
329 Server *s=new Server(d_port, d_listenaddress);
330
331 Session *client;
332 pthread_t tid;
333
334 L<<Logger::Error<<"Launched webserver on "<<d_listenaddress<<":"<<d_port<<endl;
335
336 while((client=s->accept())) {
337 pthread_create(&tid, 0 , &serveConnection, (void *)client);
338 }
339 }
340 catch(SessionTimeoutException &e) {
341 L<<Logger::Error<<"Timeout in webserver"<<endl;
342 }
343 catch(SessionException &e) {
344 L<<Logger::Error<<"Fatal error in webserver: "<<e.reason<<endl;
345 }
346 catch(Exception &e) {
347 L<<Logger::Error<<"Fatal error in main webserver thread: "<<e.reason<<endl;
348 }
349 catch(exception &e) {
350 L<<Logger::Error<<"STL Exception in main webserver thread: "<<e.what()<<endl;
351 }
352 catch(...) {
353 L<<Logger::Error<<"Unknown exception in main webserver thread"<<endl;
354 }
355 exit(1);
356
357 }