]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_httpd.cpp
Show reasons in xline expiry messages.
[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 /// $CompilerFlags: -Ivendor_directory("http_parser")
25
26
27 #include "inspircd.h"
28 #include "iohook.h"
29 #include "modules/httpd.h"
30
31 // Fix warnings about the use of commas at end of enumerator lists on C++03.
32 #if defined __clang__
33 # pragma clang diagnostic ignored "-Wc++11-extensions"
34 #elif defined __GNUC__
35 # pragma GCC diagnostic ignored "-pedantic"
36 #endif
37
38 // Fix warnings about shadowing in http_parser.
39 #pragma GCC diagnostic ignored "-Wshadow"
40
41 #include <http_parser.c>
42
43 class ModuleHttpServer;
44
45 static ModuleHttpServer* HttpModule;
46 static insp::intrusive_list<HttpServerSocket> sockets;
47 static Events::ModuleEventProvider* aclevprov;
48 static Events::ModuleEventProvider* reqevprov;
49 static http_parser_settings parser_settings;
50
51 /** A socket used for HTTP transport
52  */
53 class HttpServerSocket : public BufferedSocket, public Timer, public insp::intrusive_list_node<HttpServerSocket>
54 {
55         friend ModuleHttpServer;
56
57         http_parser parser;
58         std::string ip;
59         std::string uri;
60         HTTPHeaders headers;
61         std::string body;
62         size_t total_buffers;
63         int status_code;
64
65         /** True if this object is in the cull list
66          */
67         bool waitingcull;
68
69         bool Tick(time_t currtime) CXX11_OVERRIDE
70         {
71                 AddToCull();
72                 return false;
73         }
74
75         template<int (HttpServerSocket::*f)()>
76         static int Callback(http_parser* p)
77         {
78                 HttpServerSocket* sock = static_cast<HttpServerSocket*>(p->data);
79                 return (sock->*f)();
80         }
81
82         template<int (HttpServerSocket::*f)(const char*, size_t)>
83         static int DataCallback(http_parser* p, const char* buf, size_t len)
84         {
85                 HttpServerSocket* sock = static_cast<HttpServerSocket*>(p->data);
86                 return (sock->*f)(buf, len);
87         }
88
89         static void ConfigureParser()
90         {
91                 http_parser_settings_init(&parser_settings);
92                 parser_settings.on_message_begin = Callback<&HttpServerSocket::OnMessageBegin>;
93                 parser_settings.on_url = DataCallback<&HttpServerSocket::OnUrl>;
94                 parser_settings.on_header_field = DataCallback<&HttpServerSocket::OnHeaderField>;
95                 parser_settings.on_body = DataCallback<&HttpServerSocket::OnBody>;
96                 parser_settings.on_message_complete = Callback<&HttpServerSocket::OnMessageComplete>;
97         }
98
99         int OnMessageBegin()
100         {
101                 uri.clear();
102                 header_state = HEADER_NONE;
103                 body.clear();
104                 total_buffers = 0;
105                 return 0;
106         }
107
108         bool AcceptData(size_t len)
109         {
110                 total_buffers += len;
111                 return total_buffers < 8192;
112         }
113
114         int OnUrl(const char* buf, size_t len)
115         {
116                 if (!AcceptData(len))
117                 {
118                         status_code = HTTP_STATUS_URI_TOO_LONG;
119                         return -1;
120                 }
121                 uri.append(buf, len);
122                 return 0;
123         }
124
125         enum { HEADER_NONE, HEADER_FIELD, HEADER_VALUE } header_state;
126         std::string header_field;
127         std::string header_value;
128
129         void OnHeaderComplete()
130         {
131                 headers.SetHeader(header_field, header_value);
132                 header_field.clear();
133                 header_value.clear();
134         }
135
136         int OnHeaderField(const char* buf, size_t len)
137         {
138                 if (header_state == HEADER_VALUE)
139                         OnHeaderComplete();
140                 header_state = HEADER_FIELD;
141                 if (!AcceptData(len))
142                 {
143                         status_code = HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
144                         return -1;
145                 }
146                 header_field.append(buf, len);
147                 return 0;
148         }
149
150         int OnHeaderValue(const char* buf, size_t len)
151         {
152                 header_state = HEADER_VALUE;
153                 if (!AcceptData(len))
154                 {
155                         status_code = HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
156                         return -1;
157                 }
158                 header_value.append(buf, len);
159                 return 0;
160         }
161
162         int OnHeadersComplete()
163         {
164                 if (header_state != HEADER_NONE)
165                         OnHeaderComplete();
166                 return 0;
167         }
168
169         int OnBody(const char* buf, size_t len)
170         {
171                 if (!AcceptData(len))
172                 {
173                         status_code = HTTP_STATUS_PAYLOAD_TOO_LARGE;
174                         return -1;
175                 }
176                 body.append(buf, len);
177                 return 0;
178         }
179
180         int OnMessageComplete()
181         {
182                 ServeData();
183                 return 0;
184         }
185
186  public:
187         HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server, unsigned int timeoutsec)
188                 : BufferedSocket(newfd)
189                 , Timer(timeoutsec)
190                 , ip(IP)
191                 , status_code(0)
192                 , waitingcull(false)
193         {
194                 if ((!via->iohookprovs.empty()) && (via->iohookprovs.back()))
195                 {
196                         via->iohookprovs.back()->OnAccept(this, client, server);
197                         // IOHook may have errored
198                         if (!getError().empty())
199                         {
200                                 AddToCull();
201                                 return;
202                         }
203                 }
204
205                 parser.data = this;
206                 http_parser_init(&parser, HTTP_REQUEST);
207                 ServerInstance->Timers.AddTimer(this);
208         }
209
210         ~HttpServerSocket()
211         {
212                 sockets.erase(this);
213         }
214
215         void OnError(BufferedSocketError) CXX11_OVERRIDE
216         {
217                 AddToCull();
218         }
219
220         const char* Response(unsigned int response)
221         {
222                 switch (response)
223                 {
224 #define HTTP_STATUS_CASE(n, m, s) case n: return #s;
225                         HTTP_STATUS_MAP(HTTP_STATUS_CASE)
226                         default:
227                                 return "WTF";
228                         break;
229                 }
230         }
231
232         void SendHTTPError(unsigned int response)
233         {
234                 HTTPHeaders empty;
235                 std::string data = InspIRCd::Format(
236                         "<html><head></head><body>Server error %u: %s<br>"
237                         "<small>Powered by <a href='http://www.inspircd.org'>InspIRCd</a></small></body></html>", response, Response(response));
238
239                 SendHeaders(data.length(), response, empty);
240                 WriteData(data);
241                 Close();
242         }
243
244         void SendHeaders(unsigned long size, unsigned int response, HTTPHeaders &rheaders)
245         {
246                 WriteData(InspIRCd::Format("HTTP/%u.%u %u %s\r\n", parser.http_major ? parser.http_major : 1, parser.http_major ? parser.http_minor : 1, response, Response(response)));
247
248                 rheaders.CreateHeader("Date", InspIRCd::TimeString(ServerInstance->Time(), "%a, %d %b %Y %H:%M:%S GMT", true));
249                 rheaders.CreateHeader("Server", INSPIRCD_BRANCH);
250                 rheaders.SetHeader("Content-Length", ConvToStr(size));
251
252                 if (size)
253                         rheaders.CreateHeader("Content-Type", "text/html");
254                 else
255                         rheaders.RemoveHeader("Content-Type");
256
257                 /* Supporting Connection: keep-alive causes a whole world of hurt syncronizing timeouts,
258                  * so remove it, its not essential for what we need.
259                  */
260                 rheaders.SetHeader("Connection", "Close");
261
262                 WriteData(rheaders.GetFormattedHeaders());
263                 WriteData("\r\n");
264         }
265
266         void OnDataReady() CXX11_OVERRIDE
267         {
268                 if (parser.upgrade || HTTP_PARSER_ERRNO(&parser))
269                         return;
270                 http_parser_execute(&parser, &parser_settings, recvq.data(), recvq.size());
271                 if (parser.upgrade || HTTP_PARSER_ERRNO(&parser))
272                         SendHTTPError(status_code ? status_code : 400);
273         }
274
275         void ServeData()
276         {
277                 ModResult MOD_RESULT;
278                 std::string method = http_method_str(static_cast<http_method>(parser.method));
279                 HTTPRequest acl(method, uri, &headers, this, ip, body);
280                 FIRST_MOD_RESULT_CUSTOM(*aclevprov, HTTPACLEventListener, OnHTTPACLCheck, MOD_RESULT, (acl));
281                 if (MOD_RESULT != MOD_RES_DENY)
282                 {
283                         HTTPRequest url(method, uri, &headers, this, ip, body);
284                         FIRST_MOD_RESULT_CUSTOM(*reqevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (url));
285                         if (MOD_RESULT == MOD_RES_PASSTHRU)
286                         {
287                                 SendHTTPError(404);
288                         }
289                 }
290         }
291
292         void Page(std::stringstream* n, unsigned int response, HTTPHeaders* hheaders)
293         {
294                 SendHeaders(n->str().length(), response, *hheaders);
295                 WriteData(n->str());
296                 Close();
297         }
298
299         void AddToCull()
300         {
301                 if (waitingcull)
302                         return;
303
304                 waitingcull = true;
305                 Close();
306                 ServerInstance->GlobalCulls.AddItem(this);
307         }
308 };
309
310 class HTTPdAPIImpl : public HTTPdAPIBase
311 {
312  public:
313         HTTPdAPIImpl(Module* parent)
314                 : HTTPdAPIBase(parent)
315         {
316         }
317
318         void SendResponse(HTTPDocumentResponse& resp) CXX11_OVERRIDE
319         {
320                 resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
321         }
322 };
323
324 class ModuleHttpServer : public Module
325 {
326         HTTPdAPIImpl APIImpl;
327         unsigned int timeoutsec;
328         Events::ModuleEventProvider acleventprov;
329         Events::ModuleEventProvider reqeventprov;
330
331  public:
332         ModuleHttpServer()
333                 : APIImpl(this)
334                 , acleventprov(this, "event/http-acl")
335                 , reqeventprov(this, "event/http-request")
336         {
337                 aclevprov = &acleventprov;
338                 reqevprov = &reqeventprov;
339                 HttpServerSocket::ConfigureParser();
340         }
341
342         void init() CXX11_OVERRIDE
343         {
344                 HttpModule = this;
345         }
346
347         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
348         {
349                 ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
350                 timeoutsec = tag->getDuration("timeout", 10, 1);
351         }
352
353         ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
354         {
355                 if (!stdalgo::string::equalsci(from->bind_tag->getString("type"), "httpd"))
356                         return MOD_RES_PASSTHRU;
357
358                 sockets.push_front(new HttpServerSocket(nfd, client->addr(), from, client, server, timeoutsec));
359                 return MOD_RES_ALLOW;
360         }
361
362         void OnUnloadModule(Module* mod) CXX11_OVERRIDE
363         {
364                 for (insp::intrusive_list<HttpServerSocket>::const_iterator i = sockets.begin(); i != sockets.end(); )
365                 {
366                         HttpServerSocket* sock = *i;
367                         ++i;
368                         if (sock->GetModHook(mod))
369                         {
370                                 sock->cull();
371                                 delete sock;
372                         }
373                 }
374         }
375
376         CullResult cull() CXX11_OVERRIDE
377         {
378                 for (insp::intrusive_list<HttpServerSocket>::const_iterator i = sockets.begin(); i != sockets.end(); ++i)
379                 {
380                         HttpServerSocket* sock = *i;
381                         sock->AddToCull();
382                 }
383                 return Module::cull();
384         }
385
386         Version GetVersion() CXX11_OVERRIDE
387         {
388                 return Version("Provides HTTP serving facilities to modules", VF_VENDOR);
389         }
390 };
391
392 MODULE_INIT(ModuleHttpServer)