Limit the number of message targes, and suppress duplicates

This prevents an user from flooding the server using commands like this:

  PRIVMSG nick1,nick1,nick1,...

Duplicate targets are suppressed silently (channels and clients).

In addition, the maximum number of targets per PRIVMSG/NOTICE/... command
are limited to MAX_HNDL_TARGETS (25). If there are more, the daemon sends
the new 407 (ERR_TOOMANYTARGETS_MSG) numeric, containing the first target
that hasn't been handled any more.

Closes #187.
This commit is contained in:
Alexander Barton 2016-01-04 22:11:47 +01:00
parent cedba36965
commit 49ab79d0e6
4 changed files with 30 additions and 8 deletions

View File

@ -206,6 +206,9 @@
/** Max. number of channel modes with arguments per MODE command. */ /** Max. number of channel modes with arguments per MODE command. */
#define MAX_HNDL_MODES_ARG 5 #define MAX_HNDL_MODES_ARG 5
/** Max. number of targets per PRIVMSG/NOTICE/... command. */
#define MAX_HNDL_TARGETS 25
/** Max. number of WHO replies. */ /** Max. number of WHO replies. */
#define MAX_RPL_WHO 25 #define MAX_RPL_WHO 25

View File

@ -517,8 +517,10 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
CL2CHAN *cl2chan; CL2CHAN *cl2chan;
CHANNEL *chan; CHANNEL *chan;
char *currentTarget = Req->argv[0]; char *currentTarget = Req->argv[0];
char *lastCurrentTarget = NULL; char *strtok_last = NULL;
char *message = NULL; char *message = NULL;
char *targets[MAX_HNDL_TARGETS];
int i, target_nr = 0;
assert(Client != NULL); assert(Client != NULL);
assert(Req != NULL); assert(Req != NULL);
@ -558,10 +560,17 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
message = Req->argv[1]; message = Req->argv[1];
/* handle msgtarget = msgto *("," msgto) */ /* handle msgtarget = msgto *("," msgto) */
currentTarget = strtok_r(currentTarget, ",", &lastCurrentTarget); currentTarget = strtok_r(currentTarget, ",", &strtok_last);
ngt_UpperStr(Req->command); ngt_UpperStr(Req->command);
while (currentTarget) { while (true) {
/* Make sure that there hasn't been such a target already: */
targets[target_nr++] = currentTarget;
for(i = 0; i < target_nr - 1; i++) {
if (strcasecmp(currentTarget, targets[i]) == 0)
goto send_next_target;
}
/* Check for and handle valid <msgto> of form: /* Check for and handle valid <msgto> of form:
* RFC 2812 2.3.1: * RFC 2812 2.3.1:
* msgto = channel / ( user [ "%" host ] "@" servername ) * msgto = channel / ( user [ "%" host ] "@" servername )
@ -725,9 +734,18 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
} }
send_next_target: send_next_target:
currentTarget = strtok_r(NULL, ",", &lastCurrentTarget); currentTarget = strtok_r(NULL, ",", &strtok_last);
if (currentTarget) if (!currentTarget)
Conn_SetPenalty(Client_Conn(Client), 1); break;
Conn_SetPenalty(Client_Conn(Client), 1);
if (target_nr >= MAX_HNDL_TARGETS) {
/* Too many targets given! */
return IRC_WriteErrClient(Client,
ERR_TOOMANYTARGETS_MSG,
currentTarget);
}
} }
return CONNECTED; return CONNECTED;

View File

@ -110,6 +110,7 @@
#define ERR_CANNOTSENDTOCHAN_MSG "404 %s %s :Cannot send to channel" #define ERR_CANNOTSENDTOCHAN_MSG "404 %s %s :Cannot send to channel"
#define ERR_TOOMANYCHANNELS_MSG "405 %s %s :You have joined too many channels" #define ERR_TOOMANYCHANNELS_MSG "405 %s %s :You have joined too many channels"
#define ERR_WASNOSUCHNICK_MSG "406 %s %s :There was no such nickname" #define ERR_WASNOSUCHNICK_MSG "406 %s %s :There was no such nickname"
#define ERR_TOOMANYTARGETS_MSG "407 %s :Too many recipients"
#define ERR_NOORIGIN_MSG "409 %s :No origin specified" #define ERR_NOORIGIN_MSG "409 %s :No origin specified"
#define ERR_INVALIDCAP_MSG "410 %s %s :Invalid CAP subcommand" #define ERR_INVALIDCAP_MSG "410 %s %s :Invalid CAP subcommand"
#define ERR_NORECIPIENT_MSG "411 %s :No recipient given (%s)" #define ERR_NORECIPIENT_MSG "411 %s :No recipient given (%s)"

View File

@ -35,13 +35,13 @@ expect {
send "privmsg nick,nick :test\r" send "privmsg nick,nick :test\r"
expect { expect {
timeout { exit 1 } timeout { exit 1 }
"@* PRIVMSG nick :test\r*@* PRIVMSG nick :test" "@* PRIVMSG nick :test"
} }
send "privmsg Nick,#testChannel,nick :test\r" send "privmsg Nick,#testChannel,nick :test\r"
expect { expect {
timeout { exit 1 } timeout { exit 1 }
"@* PRIVMSG nick :test\r*401*@* PRIVMSG nick :test" "@* PRIVMSG nick :test\r*401"
} }
send "privmsg doesnotexist :test\r" send "privmsg doesnotexist :test\r"