summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorlinuxdaemon <linuxdaemon@users.noreply.github.com>2019-02-06 04:33:06 -0600
committerPeter Powell <petpow@saberuk.com>2019-02-06 10:33:06 +0000
commitd4a1ea70451abb333e71f9cff09b624db59531a0 (patch)
treec9a07b0c6ee562f2d4d76e4c797e415f943f3936 /src
parenta638de7715b55c9a09e51fd5d42d97f05d966da7 (diff)
Expand searching in m_httpd_stats, add global handling of GET parameters (#1566)
Diffstat (limited to 'src')
-rw-r--r--src/modules/m_httpd.cpp41
-rw-r--r--src/modules/m_httpd_acl.cpp8
-rw-r--r--src/modules/m_httpd_config.cpp2
-rw-r--r--src/modules/m_httpd_stats.cpp190
4 files changed, 208 insertions, 33 deletions
diff --git a/src/modules/m_httpd.cpp b/src/modules/m_httpd.cpp
index 3a0d4f861..168e09bb8 100644
--- a/src/modules/m_httpd.cpp
+++ b/src/modules/m_httpd.cpp
@@ -59,6 +59,7 @@ class HttpServerSocket : public BufferedSocket, public Timer, public insp::intru
friend ModuleHttpServer;
http_parser parser;
+ http_parser_url url;
std::string ip;
std::string uri;
HTTPHeaders headers;
@@ -280,11 +281,13 @@ class HttpServerSocket : public BufferedSocket, public Timer, public insp::intru
{
ModResult MOD_RESULT;
std::string method = http_method_str(static_cast<http_method>(parser.method));
- HTTPRequest acl(method, uri, &headers, this, ip, body);
+ HTTPRequestURI parsed;
+ ParseURI(uri, parsed);
+ HTTPRequest acl(method, parsed, &headers, this, ip, body);
FIRST_MOD_RESULT_CUSTOM(*aclevprov, HTTPACLEventListener, OnHTTPACLCheck, MOD_RESULT, (acl));
if (MOD_RESULT != MOD_RES_DENY)
{
- HTTPRequest url(method, uri, &headers, this, ip, body);
+ HTTPRequest url(method, parsed, &headers, this, ip, body);
FIRST_MOD_RESULT_CUSTOM(*reqevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (url));
if (MOD_RESULT == MOD_RES_PASSTHRU)
{
@@ -309,6 +312,40 @@ class HttpServerSocket : public BufferedSocket, public Timer, public insp::intru
Close();
ServerInstance->GlobalCulls.AddItem(this);
}
+
+ bool ParseURI(const std::string& uri, HTTPRequestURI& out)
+ {
+ http_parser_url_init(&url);
+ if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &url) != 0)
+ return false;
+
+ if (url.field_set & (1 << UF_PATH))
+ out.path = uri.substr(url.field_data[UF_PATH].off, url.field_data[UF_PATH].len);
+
+ 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.insert(std::make_pair(token, ""));
+ }
+ else
+ {
+ out.query_params.insert(std::make_pair(token.substr(0, eq_pos), token.substr(eq_pos + 1)));
+ }
+ }
+ return true;
+ }
};
class HTTPdAPIImpl : public HTTPdAPIBase
diff --git a/src/modules/m_httpd_acl.cpp b/src/modules/m_httpd_acl.cpp
index 49710c219..8052db4bc 100644
--- a/src/modules/m_httpd_acl.cpp
+++ b/src/modules/m_httpd_acl.cpp
@@ -113,7 +113,7 @@ class ModuleHTTPAccessList : public Module, public HTTPACLEventListener
for (std::vector<HTTPACL>::const_iterator this_acl = acl_list.begin(); this_acl != acl_list.end(); ++this_acl)
{
- if (InspIRCd::Match(http->GetURI(), this_acl->path, ascii_case_insensitive_map))
+ if (InspIRCd::Match(http->GetPath(), this_acl->path, ascii_case_insensitive_map))
{
if (!this_acl->blacklist.empty())
{
@@ -126,7 +126,7 @@ class ModuleHTTPAccessList : public Module, public HTTPACLEventListener
if (InspIRCd::Match(http->GetIP(), entry, ascii_case_insensitive_map))
{
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Denying access to blacklisted resource %s (matched by pattern %s) from ip %s (matched by entry %s)",
- http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), entry.c_str());
+ http->GetPath().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), entry.c_str());
BlockAccess(http, 403);
return false;
}
@@ -148,7 +148,7 @@ class ModuleHTTPAccessList : public Module, public HTTPACLEventListener
if (!allow_access)
{
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Denying access to whitelisted resource %s (matched by pattern %s) from ip %s (Not in whitelist)",
- http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str());
+ http->GetPath().c_str(), this_acl->path.c_str(), http->GetIP().c_str());
BlockAccess(http, 403);
return false;
}
@@ -157,7 +157,7 @@ class ModuleHTTPAccessList : public Module, public HTTPACLEventListener
{
/* Password auth, first look to see if we have a basic authentication header */
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Checking HTTP auth password for resource %s (matched by pattern %s) from ip %s, against username %s",
- http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), this_acl->username.c_str());
+ http->GetPath().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), this_acl->username.c_str());
if (http->headers->IsSet("Authorization"))
{
diff --git a/src/modules/m_httpd_config.cpp b/src/modules/m_httpd_config.cpp
index f729b2774..25d2f54bf 100644
--- a/src/modules/m_httpd_config.cpp
+++ b/src/modules/m_httpd_config.cpp
@@ -34,7 +34,7 @@ class ModuleHttpConfig : public Module, public HTTPRequestEventListener
ModResult OnHTTPRequest(HTTPRequest& request) CXX11_OVERRIDE
{
- if ((request.GetURI() != "/config") && (request.GetURI() != "/config/"))
+ if ((request.GetPath() != "/config") && (request.GetPath() != "/config/"))
return MOD_RES_PASSTHRU;
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling request for the HTTP /config route");
diff --git a/src/modules/m_httpd_stats.cpp b/src/modules/m_httpd_stats.cpp
index 4b18eadc4..2b28c2628 100644
--- a/src/modules/m_httpd_stats.cpp
+++ b/src/modules/m_httpd_stats.cpp
@@ -99,7 +99,7 @@ namespace Stats
{
return data << "<server><name>" << ServerInstance->Config->ServerName << "</name><description>"
<< Sanitize(ServerInstance->Config->ServerDesc) << "</description><version>"
- << Sanitize(ServerInstance->GetVersionString()) << "</version></server>";
+ << Sanitize(ServerInstance->GetVersionString(true)) << "</version></server>";
}
std::ostream& ISupport(std::ostream& data)
@@ -124,6 +124,7 @@ namespace Stats
data << "<opercount>" << ServerInstance->Users->all_opers.size() << "</opercount>";
data << "<socketcount>" << (SocketEngine::GetUsedFds()) << "</socketcount><socketmax>" << SocketEngine::GetMaxFds() << "</socketmax>";
data << "<uptime><boot_time_t>" << ServerInstance->startup_time << "</boot_time_t></uptime>";
+ data << "<currenttime>" << ServerInstance->Time() << "</currenttime>";
data << ISupport;
return data << "</general>";
@@ -201,6 +202,37 @@ namespace Stats
return data << "</channellist>";
}
+ std::ostream& DumpUser(std::ostream& data, User* u)
+ {
+ data << "<user>";
+ data << "<nickname>" << u->nick << "</nickname><uuid>" << u->uuid << "</uuid><realhost>"
+ << u->GetRealHost() << "</realhost><displayhost>" << u->GetDisplayedHost() << "</displayhost><realname>"
+ << Sanitize(u->GetRealName()) << "</realname><server>" << u->server->GetName() << "</server><signon>"
+ << u->signon << "</signon><age>" << u->age << "</age>";
+
+ if (u->IsAway())
+ data << "<away>" << Sanitize(u->awaymsg) << "</away><awaytime>" << u->awaytime << "</awaytime>";
+
+ if (u->IsOper())
+ data << "<opertype>" << Sanitize(u->oper->name) << "</opertype>";
+
+ data << "<modes>" << u->GetModeLetters().substr(1) << "</modes><ident>" << Sanitize(u->ident) << "</ident>";
+
+ LocalUser* lu = IS_LOCAL(u);
+ if (lu)
+ data << "<local/><port>" << lu->GetServerPort() << "</port><servaddr>"
+ << lu->server_sa.str() << "</servaddr><connectclass>"
+ << lu->GetClass()->GetName() << "</connectclass><lastmsg>"
+ << lu->idle_lastmsg << "</lastmsg>";
+
+ data << "<ipaddress>" << u->GetIPString() << "</ipaddress>";
+
+ DumpMeta(data, u);
+
+ data << "</user>";
+ return data;
+ }
+
std::ostream& Users(std::ostream& data)
{
data << "<userlist>";
@@ -209,24 +241,10 @@ namespace Stats
{
User* u = i->second;
- data << "<user>";
- data << "<nickname>" << u->nick << "</nickname><uuid>" << u->uuid << "</uuid><realhost>"
- << u->GetRealHost() << "</realhost><displayhost>" << u->GetDisplayedHost() << "</displayhost><realname>"
- << Sanitize(u->GetRealName()) << "</realname><server>" << u->server->GetName() << "</server>";
- if (u->IsAway())
- data << "<away>" << Sanitize(u->awaymsg) << "</away><awaytime>" << u->awaytime << "</awaytime>";
- if (u->IsOper())
- data << "<opertype>" << Sanitize(u->oper->name) << "</opertype>";
- data << "<modes>" << u->GetModeLetters().substr(1) << "</modes><ident>" << Sanitize(u->ident) << "</ident>";
- LocalUser* lu = IS_LOCAL(u);
- if (lu)
- data << "<port>" << lu->GetServerPort() << "</port><servaddr>"
- << lu->server_sa.str() << "</servaddr>";
- data << "<ipaddress>" << u->GetIPString() << "</ipaddress>";
-
- DumpMeta(data, u);
+ if (u->registered != REG_ALL)
+ continue;
- data << "</user>";
+ DumpUser(data, u);
}
return data << "</userlist>";
}
@@ -265,28 +283,145 @@ namespace Stats
}
return data << "</commandlist>";
}
+
+ enum OrderBy
+ {
+ OB_NICK,
+ OB_LASTMSG,
+
+ OB_NONE
+ };
+
+ struct UserSorter
+ {
+ OrderBy order;
+ bool desc;
+
+ UserSorter(OrderBy Order, bool Desc = false) : order(Order), desc(Desc) {}
+
+ template <typename T>
+ inline bool Compare(const T& a, const T& b)
+ {
+ return desc ? a > b : a < b;
+ }
+
+ bool operator()(User* u1, User* u2)
+ {
+ switch (order) {
+ case OB_LASTMSG:
+ return Compare(IS_LOCAL(u1)->idle_lastmsg, IS_LOCAL(u2)->idle_lastmsg);
+ break;
+ case OB_NICK:
+ return Compare(u1->nick, u2->nick);
+ break;
+ default:
+ case OB_NONE:
+ return false;
+ break;
+ }
+ }
+ };
+
+ std::ostream& ListUsers(std::ostream& data, const HTTPQueryParameters& params)
+ {
+ if (params.empty())
+ return Users(data);
+
+ data << "<userlist>";
+
+ // Filters
+ size_t limit = params.getNum<size_t>("limit");
+ bool showunreg = params.getBool("showunreg");
+ bool localonly = params.getBool("localonly");
+
+ // Minimum time since a user's last message
+ unsigned long min_idle = params.getDuration("minidle");
+ time_t maxlastmsg = ServerInstance->Time() - min_idle;
+
+ if (min_idle)
+ // We can only check idle times on local users
+ localonly = true;
+
+ // Sorting
+ const std::string& sortmethod = params.getString("sortby");
+ bool desc = params.getBool("desc", false);
+
+ OrderBy orderby;
+ if (stdalgo::string::equalsci(sortmethod, "nick"))
+ orderby = OB_NICK;
+ else if (stdalgo::string::equalsci(sortmethod, "lastmsg"))
+ {
+ orderby = OB_LASTMSG;
+ // We can only check idle times on local users
+ localonly = true;
+ }
+ else
+ orderby = OB_NONE;
+
+ typedef std::list<User*> NewUserList;
+ NewUserList user_list;
+ user_hash users = ServerInstance->Users->GetUsers();
+ for (user_hash::iterator i = users.begin(); i != users.end(); ++i)
+ {
+ User* u = i->second;
+ if (!showunreg && u->registered != REG_ALL)
+ continue;
+
+ LocalUser* lu = IS_LOCAL(u);
+ if (localonly && !lu)
+ continue;
+
+ if (min_idle && lu->idle_lastmsg > maxlastmsg)
+ continue;
+
+ user_list.push_back(u);
+ }
+
+ UserSorter sorter(orderby, desc);
+ if (sorter.order != OB_NONE && !(!localonly && sorter.order == OB_LASTMSG))
+ user_list.sort(sorter);
+
+ size_t count = 0;
+ for (NewUserList::const_iterator i = user_list.begin(); i != user_list.end() && (!limit || count < limit); ++i, ++count)
+ DumpUser(data, *i);
+
+ data << "</userlist>";
+ return data;
+ }
}
class ModuleHttpStats : public Module, public HTTPRequestEventListener
{
HTTPdAPI API;
+ bool enableparams;
public:
ModuleHttpStats()
: HTTPRequestEventListener(this)
, API(this)
+ , enableparams(false)
{
}
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* conf = ServerInstance->Config->ConfValue("httpstats");
+
+ // Parameterized queries may cause a performance issue
+ // Due to the sheer volume of data
+ // So default them to disabled
+ enableparams = conf->getBool("enableparams");
+ }
+
ModResult HandleRequest(HTTPRequest* http)
{
- std::string uri = http->GetURI();
+ std::string path = http->GetPath();
- if (uri != "/stats" && uri.substr(0, 7) != "/stats/")
+ if (path != "/stats" && path.substr(0, 7) != "/stats/")
return MOD_RES_PASSTHRU;
- if (uri[uri.size() - 1] == '/')
- uri.erase(uri.size() - 1, 1);
+ if (path[path.size() - 1] == '/')
+ path.erase(path.size() - 1, 1);
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Handling httpd event");
@@ -294,20 +429,23 @@ class ModuleHttpStats : public Module, public HTTPRequestEventListener
std::stringstream data;
data << "<inspircdstats>";
- if (uri == "/stats")
+ if (path == "/stats")
{
data << Stats::ServerInfo << Stats::General
<< Stats::XLines << Stats::Modules
<< Stats::Channels << Stats::Users
<< Stats::Servers << Stats::Commands;
}
- else if (uri == "/stats/general")
+ else if (path == "/stats/general")
{
data << Stats::General;
}
- else if (uri == "/stats/users")
+ else if (path == "/stats/users")
{
- data << Stats::Users;
+ if (enableparams)
+ Stats::ListUsers(data, http->GetParsedURI().query_params);
+ else
+ data << Stats::Users;
}
else
{