]>
Commit | Line | Data |
---|---|---|
12c86877 BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
ac7ba905 | 3 | Copyright (C) 2002-2012 PowerDNS.COM BV |
12c86877 BH |
4 | |
5 | This program is free software; you can redistribute it and/or modify | |
22dc646a BH |
6 | it under the terms of the GNU General Public License version 2 |
7 | as published by the Free Software Foundation | |
f782fe38 MH |
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. | |
12c86877 BH |
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 | |
06bd9ccf | 20 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
12c86877 | 21 | */ |
731f58b8 | 22 | #include "utility.hh" |
12c86877 BH |
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" | |
2db9c30e | 30 | #include "base64.hh" |
33196945 | 31 | #include "json.hh" |
12c86877 BH |
32 | |
33 | ||
34 | map<string,WebServer::HandlerFunction *>WebServer::d_functions; | |
35 | void *WebServer::d_that; | |
36 | string WebServer::d_password; | |
37 | ||
12c86877 BH |
38 | int WebServer::B64Decode(const std::string& strInput, std::string& strOutput) |
39 | { | |
2db9c30e | 40 | return ::B64Decode(strInput, strOutput); |
12c86877 BH |
41 | } |
42 | ||
12c86877 BH |
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) | |
33196945 | 54 | try { |
6b70b8c7 | 55 | pthread_detach(pthread_self()); |
12c86877 | 56 | Session *client=static_cast<Session *>(p); |
33196945 CH |
57 | bool want_html=false; |
58 | bool want_json=false; | |
59 | ||
12c86877 BH |
60 | try { |
61 | string line; | |
9761df50 | 62 | client->setTimeout(5); |
12c86877 BH |
63 | client->getLine(line); |
64 | stripLine(line); | |
8ef98963 | 65 | if(line.empty()) |
33196945 | 66 | throw HttpBadRequestException(); |
12c86877 BH |
67 | // L<<"page: "<<line<<endl; |
68 | ||
69 | vector<string> parts; | |
70 | stringtok(parts,line); | |
71 | ||
a2ce158c BH |
72 | string method, uri; |
73 | if(parts.size()>1) { | |
74 | method=parts[0]; | |
12c86877 | 75 | uri=parts[1]; |
a2ce158c | 76 | } |
12c86877 BH |
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(); | |
4957a608 | 102 | i!=variables.end();++i) { |
12c86877 BH |
103 | |
104 | parts.clear(); | |
105 | stringtok(parts,*i,"="); | |
106 | if(parts.size()>1) | |
4957a608 | 107 | varmap[parts[0]]=parts[1]; |
12c86877 | 108 | else |
4957a608 | 109 | varmap[parts[0]]=""; |
12c86877 | 110 | |
12c86877 BH |
111 | } |
112 | ||
113 | bool authOK=0; | |
a2ce158c | 114 | int postlen = 0; |
12c86877 BH |
115 | // read & ignore other lines |
116 | do { | |
117 | client->getLine(line); | |
118 | stripLine(line); | |
119 | ||
33196945 CH |
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); | |
4957a608 BH |
132 | string plain; |
133 | ||
134 | B64Decode(cookie,plain); | |
135 | vector<string>cparts; | |
136 | stringtok(cparts,plain,":"); | |
33196945 | 137 | // L<<Logger::Error<<"Entered password: '"<<cparts[1].c_str()<<"', should be '"<<d_password.c_str()<<"'"<<endl; |
4957a608 BH |
138 | if(cparts.size()==2 && !strcmp(cparts[1].c_str(),d_password.c_str())) { // this gets rid of terminating zeros |
139 | authOK=1; | |
140 | } | |
12c86877 | 141 | } |
33196945 CH |
142 | else if(header == "content-length" && method=="POST") { |
143 | postlen = atoi(value.c_str()); | |
a2ce158c BH |
144 | // cout<<"Got a post: "<<postlen<<" bytes"<<endl; |
145 | } | |
33196945 CH |
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 | } | |
a2ce158c BH |
154 | else |
155 | ; // cerr<<"Ignoring line: "<<line<<endl; | |
156 | ||
33196945 | 157 | } while(true); |
12c86877 | 158 | |
a2ce158c BH |
159 | string post; |
160 | if(postlen) | |
161 | post = client->get(postlen); | |
162 | ||
163 | // cout<<"Post: '"<<post<<"'"<<endl; | |
164 | ||
33196945 CH |
165 | if(!d_password.empty() && !authOK) |
166 | throw HttpUnauthorizedException(); | |
12c86877 BH |
167 | |
168 | HandlerFunction *fptr; | |
f1f85f12 | 169 | if(d_functions.count(baseUrl) && (fptr=d_functions[baseUrl])) { |
117d4c45 | 170 | bool custom=false; |
a2ce158c | 171 | string ret=(*fptr)(method, post, varmap, d_that, &custom); |
12c86877 BH |
172 | |
173 | if(!custom) { | |
4957a608 BH |
174 | client->putLine("HTTP/1.1 200 OK\n"); |
175 | client->putLine("Connection: close\n"); | |
e24e563c | 176 | client->putLine("Content-Type: text/html; charset=utf-8\n\n"); |
12c86877 BH |
177 | } |
178 | client->putLine(ret); | |
179 | } | |
180 | else { | |
33196945 | 181 | throw HttpNotFoundException(); |
12c86877 | 182 | } |
12c86877 BH |
183 | |
184 | } | |
33196945 CH |
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 | } | |
12c86877 | 199 | } |
33196945 CH |
200 | |
201 | client->close(); | |
202 | delete client; | |
203 | client=0; | |
204 | ||
12c86877 BH |
205 | return 0; |
206 | } | |
33196945 CH |
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 | ||
12c86877 BH |
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; | |
96d299db BH |
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 | } | |
12c86877 BH |
233 | } |
234 | ||
235 | void WebServer::go() | |
236 | { | |
96d299db BH |
237 | if(!d_server) |
238 | return; | |
12c86877 | 239 | try { |
12c86877 BH |
240 | Session *client; |
241 | pthread_t tid; | |
242 | ||
96a2625e | 243 | L<<Logger::Error<<"Launched webserver on " << d_server->d_local.toStringWithPort() <<endl; |
12c86877 | 244 | |
96d299db | 245 | while((client=d_server->accept())) { |
12c86877 BH |
246 | pthread_create(&tid, 0 , &serveConnection, (void *)client); |
247 | } | |
248 | } | |
249 | catch(SessionTimeoutException &e) { | |
96a2625e | 250 | // L<<Logger::Error<<"Timeout in webserver"<<endl; |
12c86877 | 251 | } |
1270deb0 CH |
252 | catch(PDNSException &e) { |
253 | L<<Logger::Error<<"Exception in main webserver thread: "<<e.reason<<endl; | |
254 | } | |
adc10f99 | 255 | catch(std::exception &e) { |
12c86877 BH |
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 | } |