aboutsummaryrefslogtreecommitdiffstats
path: root/src/modules/m_httpd.cpp
diff options
context:
space:
mode:
authorGravatar Sadie Powell2024-07-17 00:06:50 +0100
committerGravatar Sadie Powell2024-07-17 00:06:50 +0100
commit889d521e05f2e922683d48c74eb00290e7a2f548 (patch)
treeb4c56bd4cdf0881601c7e4d6ff571e491af10bf4 /src/modules/m_httpd.cpp
parentUse std::endian from C++20 in the sha1 module. (diff)
downloadinspircd++-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.cpp471
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)