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