diff options
| author | 2024-07-17 00:06:50 +0100 | |
|---|---|---|
| committer | 2024-07-17 00:06:50 +0100 | |
| commit | 889d521e05f2e922683d48c74eb00290e7a2f548 (patch) | |
| tree | b4c56bd4cdf0881601c7e4d6ff571e491af10bf4 /src/modules/m_httpd.cpp | |
| parent | Use std::endian from C++20 in the sha1 module. (diff) | |
| download | inspircd++-889d521e05f2e922683d48c74eb00290e7a2f548.tar.gz inspircd++-889d521e05f2e922683d48c74eb00290e7a2f548.tar.bz2 inspircd++-889d521e05f2e922683d48c74eb00290e7a2f548.zip | |
Shuffle the modules about a bit.
Diffstat (limited to 'src/modules/m_httpd.cpp')
| -rw-r--r-- | src/modules/m_httpd.cpp | 471 |
1 files changed, 0 insertions, 471 deletions
diff --git a/src/modules/m_httpd.cpp b/src/modules/m_httpd.cpp deleted file mode 100644 index de80b63ed..000000000 --- a/src/modules/m_httpd.cpp +++ /dev/null @@ -1,471 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2021 Dominic Hamon - * Copyright (C) 2019 linuxdaemon <linuxdaemon.irc@gmail.com> - * Copyright (C) 2018 edef <edef@edef.eu> - * Copyright (C) 2013-2014, 2017, 2019-2023 Sadie Powell <sadie@witchery.services> - * Copyright (C) 2012-2016 Attila Molnar <attilamolnar@hush.com> - * Copyright (C) 2012 Robby <robby@chatbelgie.be> - * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org> - * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net> - * Copyright (C) 2007 John Brooks <john@jbrooks.io> - * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org> - * Copyright (C) 2006, 2008 Craig Edwards <brain@inspircd.org> - * - * This file is part of InspIRCd. InspIRCd is free software: you can - * redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, version 2. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - - -#include "inspircd.h" -#include "iohook.h" -#include "modules/httpd.h" -#include "utility/string.h" - -#ifdef __GNUC__ -# pragma GCC diagnostic push -#endif - -// Fix warnings about shadowing in http_parser. -#ifdef __GNUC__ -# pragma GCC diagnostic ignored "-Wshadow" -#endif - -#include <http_parser/http_parser.c> - -#ifdef __GNUC__ -# pragma GCC diagnostic pop -#endif - -class ModuleHttpServer; - -static ModuleHttpServer* HttpModule; -static insp::intrusive_list<HttpServerSocket> sockets; -static Events::ModuleEventProvider* aclevprov; -static Events::ModuleEventProvider* reqevprov; -static http_parser_settings parser_settings; - -/** A socket used for HTTP transport - */ -class HttpServerSocket final - : public BufferedSocket - , public Timer - , public insp::intrusive_list_node<HttpServerSocket> -{ -private: - friend class ModuleHttpServer; - - http_parser parser; - http_parser_url url; - std::string ip; - std::string uri; - HTTPHeaders headers; - std::string body; - size_t total_buffers; - int status_code = 0; - - /** True if this object is in the cull list - */ - bool waitingcull = false; - bool messagecomplete = false; - - bool Tick() override - { - if (!messagecomplete) - { - ServerInstance->Logs.Debug(MODNAME, "HTTP socket {} timed out", GetFd()); - Close(); - return false; - } - - return true; - } - - template<int (HttpServerSocket::*f)()> - static int Callback(http_parser* p) - { - HttpServerSocket* sock = static_cast<HttpServerSocket*>(p->data); - return (sock->*f)(); - } - - template<int (HttpServerSocket::*f)(const char*, size_t)> - static int DataCallback(http_parser* p, const char* buf, size_t len) - { - HttpServerSocket* sock = static_cast<HttpServerSocket*>(p->data); - return (sock->*f)(buf, len); - } - - static void ConfigureParser() - { - http_parser_settings_init(&parser_settings); - parser_settings.on_message_begin = Callback<&HttpServerSocket::OnMessageBegin>; - parser_settings.on_url = DataCallback<&HttpServerSocket::OnUrl>; - parser_settings.on_header_field = DataCallback<&HttpServerSocket::OnHeaderField>; - parser_settings.on_header_value = DataCallback<&HttpServerSocket::OnHeaderValue>; - parser_settings.on_headers_complete = Callback<&HttpServerSocket::OnHeadersComplete>; - parser_settings.on_body = DataCallback<&HttpServerSocket::OnBody>; - parser_settings.on_message_complete = Callback<&HttpServerSocket::OnMessageComplete>; - } - - int OnMessageBegin() - { - uri.clear(); - header_state = HEADER_NONE; - body.clear(); - total_buffers = 0; - return 0; - } - - bool AcceptData(size_t len) - { - total_buffers += len; - return total_buffers < 8192; - } - - int OnUrl(const char* buf, size_t len) - { - if (!AcceptData(len)) - { - status_code = HTTP_STATUS_URI_TOO_LONG; - return -1; - } - uri.append(buf, len); - return 0; - } - - enum { HEADER_NONE, HEADER_FIELD, HEADER_VALUE } header_state; - std::string header_field; - std::string header_value; - - void OnHeaderComplete() - { - headers.SetHeader(header_field, header_value); - header_field.clear(); - header_value.clear(); - } - - int OnHeaderField(const char* buf, size_t len) - { - if (header_state == HEADER_VALUE) - OnHeaderComplete(); - header_state = HEADER_FIELD; - if (!AcceptData(len)) - { - status_code = HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE; - return -1; - } - header_field.append(buf, len); - return 0; - } - - int OnHeaderValue(const char* buf, size_t len) - { - header_state = HEADER_VALUE; - if (!AcceptData(len)) - { - status_code = HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE; - return -1; - } - header_value.append(buf, len); - return 0; - } - - int OnHeadersComplete() - { - if (header_state != HEADER_NONE) - OnHeaderComplete(); - return 0; - } - - int OnBody(const char* buf, size_t len) - { - if (!AcceptData(len)) - { - status_code = HTTP_STATUS_PAYLOAD_TOO_LARGE; - return -1; - } - body.append(buf, len); - return 0; - } - - int OnMessageComplete() - { - messagecomplete = true; - ServeData(); - return 0; - } - -public: - HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, const irc::sockets::sockaddrs& client, const irc::sockets::sockaddrs& server, unsigned long timeoutsec) - : BufferedSocket(newfd) - , Timer(timeoutsec, false) - , ip(IP) - { - if ((!via->iohookprovs.empty()) && (via->iohookprovs.back())) - { - via->iohookprovs.back()->OnAccept(this, client, server); - // IOHook may have errored - if (!GetError().empty()) - { - ServerInstance->Logs.Debug(MODNAME, "HTTP socket {} encountered a hook error: {}", - GetFd(), GetError()); - Close(); - return; - } - } - - parser.data = this; - http_parser_init(&parser, HTTP_REQUEST); - ServerInstance->Timers.AddTimer(this); - } - - ~HttpServerSocket() override - { - sockets.erase(this); - } - - void Close() override - { - if (waitingcull || !HasFd()) - return; - - waitingcull = true; - BufferedSocket::Close(); - ServerInstance->GlobalCulls.AddItem(this); - } - - void OnError(BufferedSocketError err) override - { - ServerInstance->Logs.Debug(MODNAME, "HTTP socket {} encountered an error: {} - {}", - GetFd(), (int)err, GetError()); - Close(); - } - - void SendHTTPError(unsigned int response, const char* errstr = nullptr) - { - if (!errstr) - errstr = http_status_str((http_status)response); - - ServerInstance->Logs.Debug(MODNAME, "Sending HTTP error {}: {}", response, errstr); - static HTTPHeaders empty; - std::string data = fmt::format( - "<html><head></head><body style='font-family: sans-serif; text-align: center'>" - "<h1 style='font-size: 48pt'>Error {}</h1><h2 style='font-size: 24pt'>{}</h2><hr>" - "<small>Powered by <a href='https://www.inspircd.org'>InspIRCd</a></small></body></html>", - response, errstr); - - Page(data, response, &empty); - } - - void SendHeaders(unsigned long size, unsigned int response, HTTPHeaders& rheaders) - { - WriteData(fmt::format("HTTP/{}.{} {} {}\r\n", parser.http_major ? parser.http_major : 1, parser.http_major ? parser.http_minor : 1, response, http_status_str((http_status)response))); - - rheaders.CreateHeader("Date", Time::ToString(ServerInstance->Time(), "%a, %d %b %Y %H:%M:%S GMT", true)); - rheaders.CreateHeader("Server", INSPIRCD_BRANCH); - rheaders.SetHeader("Content-Length", ConvToStr(size)); - - if (size) - rheaders.CreateHeader("Content-Type", "text/html"); - else - rheaders.RemoveHeader("Content-Type"); - - /* Supporting Connection: keep-alive causes a whole world of hurt synchronizing timeouts, - * so remove it, its not essential for what we need. - */ - rheaders.SetHeader("Connection", "Close"); - - WriteData(rheaders.GetFormattedHeaders()); - WriteData("\r\n"); - } - - void OnDataReady() override - { - if (parser.upgrade || HTTP_PARSER_ERRNO(&parser)) - return; - http_parser_execute(&parser, &parser_settings, recvq.data(), recvq.size()); - if (parser.upgrade) - SendHTTPError(status_code ? status_code : 400); - else if (HTTP_PARSER_ERRNO(&parser)) - SendHTTPError(status_code ? status_code : 400, http_errno_description((http_errno)parser.http_errno)); - } - - void ServeData() - { - std::string method = http_method_str(static_cast<http_method>(parser.method)); - HTTPRequestURI parsed; - ParseURI(uri, parsed); - HTTPRequest acl(method, parsed, &headers, this, ip, body); - ModResult res = aclevprov->FirstResult(&HTTPACLEventListener::OnHTTPACLCheck, acl); - if (res != MOD_RES_DENY) - { - HTTPRequest request(method, parsed, &headers, this, ip, body); - res = reqevprov->FirstResult(&HTTPRequestEventListener::OnHTTPRequest, request); - if (res == MOD_RES_PASSTHRU) - { - SendHTTPError(404); - } - } - } - - void Page(const std::string& s, unsigned int response, HTTPHeaders* hheaders) - { - SendHeaders(s.length(), response, *hheaders); - WriteData(s); - BufferedSocket::Close(true); - } - - void Page(std::stringstream* n, unsigned int response, HTTPHeaders* hheaders) - { - Page(n->str(), response, hheaders); - } - - bool ParseURI(const std::string& uristr, HTTPRequestURI& out) - { - http_parser_url_init(&url); - if (http_parser_parse_url(uristr.c_str(), uristr.size(), 0, &url) != 0) - return false; - - if (url.field_set & (1 << UF_PATH)) - { - // Normalise the path. - std::vector<std::string> pathsegments; - irc::sepstream pathstream(uri.substr(url.field_data[UF_PATH].off, url.field_data[UF_PATH].len), '/'); - for (std::string pathsegment; pathstream.GetToken(pathsegment); ) - { - if (pathsegment == ".") - { - // Stay at the current level. - continue; - } - - if (pathsegment == "..") - { - // Traverse up to the previous level. - if (!pathsegments.empty()) - pathsegments.pop_back(); - continue; - } - - pathsegments.push_back(pathsegment); - } - - out.path.reserve(url.field_data[UF_PATH].len); - out.path.append("/").append(insp::join(pathsegments, '/')); - } - - if (url.field_set & (1 << UF_FRAGMENT)) - out.fragment = uri.substr(url.field_data[UF_FRAGMENT].off, url.field_data[UF_FRAGMENT].len); - - std::string param_str; - if (url.field_set & (1 << UF_QUERY)) - param_str = uri.substr(url.field_data[UF_QUERY].off, url.field_data[UF_QUERY].len); - - irc::sepstream param_stream(param_str, '&'); - std::string token; - std::string::size_type eq_pos; - while (param_stream.GetToken(token)) - { - eq_pos = token.find('='); - if (eq_pos == std::string::npos) - { - out.query_params.emplace(token, ""); - } - else - { - out.query_params.emplace(token.substr(0, eq_pos), token.substr(eq_pos + 1)); - } - } - return true; - } -}; - -class HTTPdAPIImpl final - : public HTTPdAPIBase -{ -public: - HTTPdAPIImpl(Module* parent) - : HTTPdAPIBase(parent) - { - } - - void SendResponse(HTTPDocumentResponse& resp) override - { - resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers); - } -}; - -class ModuleHttpServer final - : public Module -{ -private: - HTTPdAPIImpl APIImpl; - unsigned long timeoutsec; - Events::ModuleEventProvider acleventprov; - Events::ModuleEventProvider reqeventprov; - -public: - ModuleHttpServer() - : Module(VF_VENDOR, "Allows the server administrator to serve various useful resources over HTTP.") - , APIImpl(this) - , acleventprov(this, "event/http-acl") - , reqeventprov(this, "event/http-request") - { - aclevprov = &acleventprov; - reqevprov = &reqeventprov; - HttpServerSocket::ConfigureParser(); - } - - void init() override - { - HttpModule = this; - } - - void ReadConfig(ConfigStatus& status) override - { - const auto& tag = ServerInstance->Config->ConfValue("httpd"); - timeoutsec = tag->getDuration("timeout", 10, 1); - } - - ModResult OnAcceptConnection(int nfd, ListenSocket* from, const irc::sockets::sockaddrs& client, const irc::sockets::sockaddrs& server) override - { - if (!insp::equalsci(from->bind_tag->getString("type"), "httpd")) - return MOD_RES_PASSTHRU; - - sockets.push_front(new HttpServerSocket(nfd, client.addr(), from, client, server, timeoutsec)); - return MOD_RES_ALLOW; - } - - void OnUnloadModule(Module* mod) override - { - for (insp::intrusive_list<HttpServerSocket>::const_iterator i = sockets.begin(); i != sockets.end(); ) - { - HttpServerSocket* sock = *i; - ++i; - if (sock->GetModHook(mod)) - { - sock->Cull(); - delete sock; - } - } - } - - Cullable::Result Cull() override - { - for (auto* sock : sockets) - sock->Close(); - return Module::Cull(); - } -}; - -MODULE_INIT(ModuleHttpServer) |
