]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_httpd.cpp
735551dffa3ba6dd184a014fa72200b99560d961
[user/henk/code/inspircd.git] / src / modules / m_httpd.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
6  *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
7  *   Copyright (C) 2006-2008 Craig Edwards <craigedwards@brainbox.cc>
8  *   Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
9  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
10  *
11  * This file is part of InspIRCd.  InspIRCd is free software: you can
12  * redistribute it and/or modify it under the terms of the GNU General Public
13  * License as published by the Free Software Foundation, version 2.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24
25 #include "inspircd.h"
26 #include "iohook.h"
27 #include "modules/httpd.h"
28
29 class ModuleHttpServer;
30
31 static ModuleHttpServer* HttpModule;
32 static bool claimed;
33 static std::set<HttpServerSocket*> sockets;
34
35 /** HTTP socket states
36  */
37 enum HttpState
38 {
39         HTTP_SERVE_WAIT_REQUEST = 0, /* Waiting for a full request */
40         HTTP_SERVE_RECV_POSTDATA = 1, /* Waiting to finish recieving POST data */
41         HTTP_SERVE_SEND_DATA = 2 /* Sending response */
42 };
43
44 /** A socket used for HTTP transport
45  */
46 class HttpServerSocket : public BufferedSocket
47 {
48         HttpState InternalState;
49         std::string ip;
50
51         HTTPHeaders headers;
52         std::string reqbuffer;
53         std::string postdata;
54         unsigned int postsize;
55         std::string request_type;
56         std::string uri;
57         std::string http_version;
58
59  public:
60         const time_t createtime;
61
62         HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
63                 : BufferedSocket(newfd), ip(IP), postsize(0)
64                 , createtime(ServerInstance->Time())
65         {
66                 InternalState = HTTP_SERVE_WAIT_REQUEST;
67
68                 FOREACH_MOD(OnHookIO, (this, via));
69                 if (GetIOHook())
70                         GetIOHook()->OnStreamSocketAccept(this, client, server);
71         }
72
73         ~HttpServerSocket()
74         {
75                 sockets.erase(this);
76         }
77
78         void OnError(BufferedSocketError) CXX11_OVERRIDE
79         {
80                 ServerInstance->GlobalCulls.AddItem(this);
81         }
82
83         std::string Response(int response)
84         {
85                 switch (response)
86                 {
87                         case 100:
88                                 return "CONTINUE";
89                         case 101:
90                                 return "SWITCHING PROTOCOLS";
91                         case 200:
92                                 return "OK";
93                         case 201:
94                                 return "CREATED";
95                         case 202:
96                                 return "ACCEPTED";
97                         case 203:
98                                 return "NON-AUTHORITATIVE INFORMATION";
99                         case 204:
100                                 return "NO CONTENT";
101                         case 205:
102                                 return "RESET CONTENT";
103                         case 206:
104                                 return "PARTIAL CONTENT";
105                         case 300:
106                                 return "MULTIPLE CHOICES";
107                         case 301:
108                                 return "MOVED PERMENANTLY";
109                         case 302:
110                                 return "FOUND";
111                         case 303:
112                                 return "SEE OTHER";
113                         case 304:
114                                 return "NOT MODIFIED";
115                         case 305:
116                                 return "USE PROXY";
117                         case 307:
118                                 return "TEMPORARY REDIRECT";
119                         case 400:
120                                 return "BAD REQUEST";
121                         case 401:
122                                 return "UNAUTHORIZED";
123                         case 402:
124                                 return "PAYMENT REQUIRED";
125                         case 403:
126                                 return "FORBIDDEN";
127                         case 404:
128                                 return "NOT FOUND";
129                         case 405:
130                                 return "METHOD NOT ALLOWED";
131                         case 406:
132                                 return "NOT ACCEPTABLE";
133                         case 407:
134                                 return "PROXY AUTHENTICATION REQUIRED";
135                         case 408:
136                                 return "REQUEST TIMEOUT";
137                         case 409:
138                                 return "CONFLICT";
139                         case 410:
140                                 return "GONE";
141                         case 411:
142                                 return "LENGTH REQUIRED";
143                         case 412:
144                                 return "PRECONDITION FAILED";
145                         case 413:
146                                 return "REQUEST ENTITY TOO LARGE";
147                         case 414:
148                                 return "REQUEST-URI TOO LONG";
149                         case 415:
150                                 return "UNSUPPORTED MEDIA TYPE";
151                         case 416:
152                                 return "REQUESTED RANGE NOT SATISFIABLE";
153                         case 417:
154                                 return "EXPECTATION FAILED";
155                         case 500:
156                                 return "INTERNAL SERVER ERROR";
157                         case 501:
158                                 return "NOT IMPLEMENTED";
159                         case 502:
160                                 return "BAD GATEWAY";
161                         case 503:
162                                 return "SERVICE UNAVAILABLE";
163                         case 504:
164                                 return "GATEWAY TIMEOUT";
165                         case 505:
166                                 return "HTTP VERSION NOT SUPPORTED";
167                         default:
168                                 return "WTF";
169                         break;
170
171                 }
172         }
173
174         void SendHTTPError(int response)
175         {
176                 HTTPHeaders empty;
177                 std::string data = "<html><head></head><body>Server error "+ConvToStr(response)+": "+Response(response)+"<br>"+
178                                    "<small>Powered by <a href='http://www.inspircd.org'>InspIRCd</a></small></body></html>";
179
180                 SendHeaders(data.length(), response, empty);
181                 WriteData(data);
182         }
183
184         void SendHeaders(unsigned long size, int response, HTTPHeaders &rheaders)
185         {
186
187                 WriteData(http_version + " "+ConvToStr(response)+" "+Response(response)+"\r\n");
188
189                 time_t local = ServerInstance->Time();
190                 struct tm *timeinfo = gmtime(&local);
191                 char *date = asctime(timeinfo);
192                 date[strlen(date) - 1] = '\0';
193                 rheaders.CreateHeader("Date", date);
194
195                 rheaders.CreateHeader("Server", BRANCH);
196                 rheaders.SetHeader("Content-Length", ConvToStr(size));
197
198                 if (size)
199                         rheaders.CreateHeader("Content-Type", "text/html");
200                 else
201                         rheaders.RemoveHeader("Content-Type");
202
203                 /* Supporting Connection: keep-alive causes a whole world of hurt syncronizing timeouts,
204                  * so remove it, its not essential for what we need.
205                  */
206                 rheaders.SetHeader("Connection", "Close");
207
208                 WriteData(rheaders.GetFormattedHeaders());
209                 WriteData("\r\n");
210         }
211
212         void OnDataReady()
213         {
214                 if (InternalState == HTTP_SERVE_RECV_POSTDATA)
215                 {
216                         postdata.append(recvq);
217                         if (postdata.length() >= postsize)
218                                 ServeData();
219                 }
220                 else
221                 {
222                         reqbuffer.append(recvq);
223
224                         if (reqbuffer.length() >= 8192)
225                         {
226                                 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "m_httpd dropped connection due to an oversized request buffer");
227                                 reqbuffer.clear();
228                                 SetError("Buffer");
229                         }
230
231                         if (InternalState == HTTP_SERVE_WAIT_REQUEST)
232                                 CheckRequestBuffer();
233                 }
234         }
235
236         void CheckRequestBuffer()
237         {
238                 std::string::size_type reqend = reqbuffer.find("\r\n\r\n");
239                 if (reqend == std::string::npos)
240                         return;
241
242                 // We have the headers; parse them all
243                 std::string::size_type hbegin = 0, hend;
244                 while ((hend = reqbuffer.find("\r\n", hbegin)) != std::string::npos)
245                 {
246                         if (hbegin == hend)
247                                 break;
248
249                         if (request_type.empty())
250                         {
251                                 std::istringstream cheader(std::string(reqbuffer, hbegin, hend - hbegin));
252                                 cheader >> request_type;
253                                 cheader >> uri;
254                                 cheader >> http_version;
255
256                                 if (request_type.empty() || uri.empty() || http_version.empty())
257                                 {
258                                         SendHTTPError(400);
259                                         return;
260                                 }
261
262                                 hbegin = hend + 2;
263                                 continue;
264                         }
265
266                         std::string cheader = reqbuffer.substr(hbegin, hend - hbegin);
267
268                         std::string::size_type fieldsep = cheader.find(':');
269                         if ((fieldsep == std::string::npos) || (fieldsep == 0) || (fieldsep == cheader.length() - 1))
270                         {
271                                 SendHTTPError(400);
272                                 return;
273                         }
274
275                         headers.SetHeader(cheader.substr(0, fieldsep), cheader.substr(fieldsep + 2));
276
277                         hbegin = hend + 2;
278                 }
279
280                 reqbuffer.erase(0, reqend + 4);
281
282                 std::transform(request_type.begin(), request_type.end(), request_type.begin(), ::toupper);
283                 std::transform(http_version.begin(), http_version.end(), http_version.begin(), ::toupper);
284
285                 if ((http_version != "HTTP/1.1") && (http_version != "HTTP/1.0"))
286                 {
287                         SendHTTPError(505);
288                         return;
289                 }
290
291                 if (headers.IsSet("Content-Length") && (postsize = ConvToInt(headers.GetHeader("Content-Length"))) > 0)
292                 {
293                         InternalState = HTTP_SERVE_RECV_POSTDATA;
294
295                         if (reqbuffer.length() >= postsize)
296                         {
297                                 postdata = reqbuffer.substr(0, postsize);
298                                 reqbuffer.erase(0, postsize);
299                         }
300                         else if (!reqbuffer.empty())
301                         {
302                                 postdata = reqbuffer;
303                                 reqbuffer.clear();
304                         }
305
306                         if (postdata.length() >= postsize)
307                                 ServeData();
308
309                         return;
310                 }
311
312                 ServeData();
313         }
314
315         void ServeData()
316         {
317                 InternalState = HTTP_SERVE_SEND_DATA;
318
319                 claimed = false;
320                 HTTPRequest acl((Module*)HttpModule, "httpd_acl", request_type, uri, &headers, this, ip, postdata);
321                 acl.Send();
322                 if (!claimed)
323                 {
324                         HTTPRequest url((Module*)HttpModule, "httpd_url", request_type, uri, &headers, this, ip, postdata);
325                         url.Send();
326                         if (!claimed)
327                         {
328                                 SendHTTPError(404);
329                         }
330                 }
331         }
332
333         void Page(std::stringstream* n, int response, HTTPHeaders *hheaders)
334         {
335                 SendHeaders(n->str().length(), response, *hheaders);
336                 WriteData(n->str());
337         }
338 };
339
340 class HTTPdAPIImpl : public HTTPdAPIBase
341 {
342  public:
343         HTTPdAPIImpl(Module* parent)
344                 : HTTPdAPIBase(parent)
345         {
346         }
347
348         void SendResponse(HTTPDocumentResponse& resp) CXX11_OVERRIDE
349         {
350                 claimed = true;
351                 resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
352         }
353 };
354
355 class ModuleHttpServer : public Module
356 {
357         std::vector<HttpServerSocket *> httpsocks;
358         HTTPdAPIImpl APIImpl;
359         unsigned int timeoutsec;
360
361  public:
362         ModuleHttpServer()
363                 : APIImpl(this)
364         {
365         }
366
367         void init() CXX11_OVERRIDE
368         {
369                 HttpModule = this;
370         }
371
372         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
373         {
374                 ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
375                 timeoutsec = tag->getInt("timeout");
376         }
377
378         ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
379         {
380                 if (from->bind_tag->getString("type") != "httpd")
381                         return MOD_RES_PASSTHRU;
382                 int port;
383                 std::string incomingip;
384                 irc::sockets::satoap(*client, incomingip, port);
385                 sockets.insert(new HttpServerSocket(nfd, incomingip, from, client, server));
386                 return MOD_RES_ALLOW;
387         }
388
389         void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
390         {
391                 if (!timeoutsec)
392                         return;
393
394                 time_t oldest_allowed = curtime - timeoutsec;
395                 for (std::set<HttpServerSocket*>::const_iterator i = sockets.begin(); i != sockets.end(); )
396                 {
397                         HttpServerSocket* sock = *i;
398                         ++i;
399                         if (sock->createtime < oldest_allowed)
400                         {
401                                 sock->cull();
402                                 delete sock;
403                         }
404                 }
405         }
406
407         CullResult cull() CXX11_OVERRIDE
408         {
409                 std::set<HttpServerSocket*> local;
410                 local.swap(sockets);
411                 for (std::set<HttpServerSocket*>::const_iterator i = local.begin(); i != local.end(); ++i)
412                 {
413                         HttpServerSocket* sock = *i;
414                         sock->cull();
415                         delete sock;
416                 }
417                 return Module::cull();
418         }
419
420         Version GetVersion() CXX11_OVERRIDE
421         {
422                 return Version("Provides HTTP serving facilities to modules", VF_VENDOR);
423         }
424 };
425
426 MODULE_INIT(ModuleHttpServer)