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