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