summaryrefslogtreecommitdiff
path: root/src/coremods/core_serialize_rfc.cpp
diff options
context:
space:
mode:
authorPeter Powell <petpow@saberuk.com>2019-01-23 21:45:50 +0000
committerPeter Powell <petpow@saberuk.com>2019-01-24 14:28:21 +0000
commitcbef0241a04eafe5250b75ebb3f7ef8c32ecb260 (patch)
treed856c8ac6db6cf2514bc5090e6b11f759d3c0290 /src/coremods/core_serialize_rfc.cpp
parenta7fac86ccda9e75c6f426b07faa85081f5857d33 (diff)
Implement support for the extended tag space for client tags.
Diffstat (limited to 'src/coremods/core_serialize_rfc.cpp')
-rw-r--r--src/coremods/core_serialize_rfc.cpp75
1 files changed, 55 insertions, 20 deletions
diff --git a/src/coremods/core_serialize_rfc.cpp b/src/coremods/core_serialize_rfc.cpp
index 1ba330146..23a4c2052 100644
--- a/src/coremods/core_serialize_rfc.cpp
+++ b/src/coremods/core_serialize_rfc.cpp
@@ -19,11 +19,20 @@
#include "inspircd.h"
+enum
+{
+ // From ircu.
+ ERR_INPUTTOOLONG = 417
+};
+
class RFCSerializer : public ClientProtocol::Serializer
{
- /** Maximum size of the message tags portion of the message, including the `@` and the trailing space characters.
- */
- static const std::string::size_type MAX_MESSAGE_TAG_LENGTH = 512;
+
+ /** The maximum size of client-originated message tags in an incoming message including the `@`. */
+ static const std::string::size_type MAX_CLIENT_MESSAGE_TAG_LENGTH = 4095;
+
+ /** The maximum size of server-originated message tags in an outgoing message including the `@`. */
+ static const std::string::size_type MAX_SERVER_MESSAGE_TAG_LENGTH = 511;
static void SerializeTags(const ClientProtocol::TagMap& tags, const ClientProtocol::TagSelection& tagwl, std::string& line);
@@ -47,15 +56,32 @@ bool RFCSerializer::Parse(LocalUser* user, const std::string& line, ClientProtoc
return false;
}
- ServerInstance->Logs->Log("USERINPUT", LOG_RAWIO, "C[%s] I %s", user->uuid.c_str(), line.c_str());
+ // Work out how long the message can actually be.
+ size_t maxline = ServerInstance->Config->Limits.MaxLine - start - 2;
+ if (line[start] == '@')
+ maxline += MAX_CLIENT_MESSAGE_TAG_LENGTH + 1;
- irc::tokenstream tokens(line, start);
- std::string token;
+ irc::tokenstream tokens(line, start, maxline);
+ ServerInstance->Logs->Log("USERINPUT", LOG_RAWIO, "C[%s] I %s", user->uuid.c_str(), tokens.GetMessage().c_str());
// This will always exist because of the check at the start of the function.
+ std::string token;
tokens.GetMiddle(token);
if (token[0] == '@')
{
+ // Check that the client tags fit within the client tag space.
+ if (token.length() > MAX_CLIENT_MESSAGE_TAG_LENGTH)
+ {
+ user->WriteNumeric(ERR_INPUTTOOLONG, "Input line was too long");
+ user->CommandFloodPenalty += 2000;
+ return false;
+ }
+
+ // Truncate the RFC part of the message if it is too long.
+ size_t maxrfcline = token.length() + ServerInstance->Config->Limits.MaxLine - 1;
+ if (tokens.GetMessage().length() > maxrfcline)
+ tokens.GetMessage().erase(maxrfcline);
+
// Line begins with message tags, parse them.
std::string tagval;
irc::sepstream ss(token.substr(1), ';');
@@ -78,7 +104,6 @@ bool RFCSerializer::Parse(LocalUser* user, const std::string& line, ClientProtoc
HandleTag(user, token, tagval, parseoutput.tags);
}
-
// Try to read the prefix or command name.
if (!tokens.GetMiddle(token))
{
@@ -114,17 +139,29 @@ bool RFCSerializer::Parse(LocalUser* user, const std::string& line, ClientProtoc
return true;
}
+namespace
+{
+ void CheckTagLength(std::string& line, size_t prevsize, size_t& length, size_t maxlength)
+ {
+ const std::string::size_type diffsize = line.size() - prevsize;
+ if (length + diffsize > maxlength)
+ line.erase(prevsize);
+ else
+ length += diffsize;
+ }
+}
+
void RFCSerializer::SerializeTags(const ClientProtocol::TagMap& tags, const ClientProtocol::TagSelection& tagwl, std::string& line)
{
- char prefix = '@'; // First tag name is prefixed with a '@'
+ size_t client_tag_length = 0;
+ size_t server_tag_length = 0;
for (ClientProtocol::TagMap::const_iterator i = tags.begin(); i != tags.end(); ++i)
{
if (!tagwl.IsSelected(tags, i))
continue;
const std::string::size_type prevsize = line.size();
- line.push_back(prefix);
- prefix = ';'; // Remaining tags are prefixed with ';'
+ line.push_back(prevsize ? ';' : '@');
line.append(i->first);
const std::string& val = i->second.value;
if (!val.empty())
@@ -133,16 +170,14 @@ void RFCSerializer::SerializeTags(const ClientProtocol::TagMap& tags, const Clie
line.append(val);
}
- // The tags part of the message mustn't grow longer than what is allowed by the spec. If it does,
- // remove last tag and stop adding more tags.
- //
- // One is subtracted from the limit before comparing because there must be a ' ' char after the last tag
- // which also counts towards the limit.
- if (line.size() > MAX_MESSAGE_TAG_LENGTH-1)
- {
- line.erase(prevsize);
- break;
- }
+ // The tags part of the message must not contain more client and server tags than allowed by the
+ // message tags specification. This is complicated by the tag space having separate limits for
+ // both server-originated and client-originated tags. If either of the tag limits is exceeded then
+ // the most recently added tag is removed.
+ if (i->first[0] == '+')
+ CheckTagLength(line, prevsize, client_tag_length, MAX_CLIENT_MESSAGE_TAG_LENGTH);
+ else
+ CheckTagLength(line, prevsize, server_tag_length, MAX_SERVER_MESSAGE_TAG_LENGTH);
}
if (!line.empty())