summaryrefslogtreecommitdiff
path: root/src/map/clif.c
diff options
context:
space:
mode:
authorultramage <ultramage@54d463be-8e91-2dee-dedb-b68131a5f0ec>2007-09-22 11:02:26 +0000
committerultramage <ultramage@54d463be-8e91-2dee-dedb-b68131a5f0ec>2007-09-22 11:02:26 +0000
commitb826bc2c54dbe6a2ad0f204f36f82bf116d8e663 (patch)
tree9edb0690c8a2ecfeb6db3723d397f6fbb4f53967 /src/map/clif.c
parent5bcbc87fbfe3d2b9182d6e7edd84083758b880a5 (diff)
downloadhercules-b826bc2c54dbe6a2ad0f204f36f82bf116d8e663.tar.gz
hercules-b826bc2c54dbe6a2ad0f204f36f82bf116d8e663.tar.bz2
hercules-b826bc2c54dbe6a2ad0f204f36f82bf116d8e663.tar.xz
hercules-b826bc2c54dbe6a2ad0f204f36f82bf116d8e663.zip
* Added 'safestrnlen' to prevent null pointer crashes
* Fixed global chat logging always crashing on a null pointer * Applied changes to clif_parse_globalmessage() from my WiP code - clearer processing of the individual packet components - proper code ordering, some more integrity checks - fixes to some poorly chosen ShowWarning() format strings - global chat logging no longer logs the entire string (w/ player name) git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@11271 54d463be-8e91-2dee-dedb-b68131a5f0ec
Diffstat (limited to 'src/map/clif.c')
-rw-r--r--src/map/clif.c111
1 files changed, 63 insertions, 48 deletions
diff --git a/src/map/clif.c b/src/map/clif.c
index fab1e00ad..5c3b62f23 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -8384,66 +8384,80 @@ void clif_parse_GetCharNameRequest(int fd, struct map_session_data *sd)
/*==========================================
* Validates and processes global messages
- * S 008c/00f3 <packet len>.w <strz>.?B
+ * S 008c/00f3 <packet len>.w <text>.?B (<name> : <message>)
*------------------------------------------*/
void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
{
- char* message;
- unsigned int packetlen, messagelen, namelen;
+ const char *text, *name, *message;
+ unsigned int packetlen, textlen, namelen, messagelen;
packetlen = RFIFOW(fd,2);
- if (packetlen > RFIFOREST(fd)) { // there has to be enough data to read
+ // basic structure checks
+ if( packetlen > RFIFOREST(fd) )
+ { // there has to be enough data to read
ShowWarning("clif_parse_GlobalMessage: Received malformed packet from player '%s' (length is incorrect)!", sd->status.name);
return;
}
- if (packetlen < 4 + 1) { // 4-byte header and at least an empty string is expected
+ if( packetlen < 4 + 1 )
+ { // 4-byte header and at least an empty string is expected
ShowWarning("clif_parse_GlobalMessage: Received malformed packet from player '%s' (no message data)!", sd->status.name);
return;
}
- message = (char*)RFIFOP(fd,4);
- messagelen = packetlen - 4; // let's trust the client here, nothing can go wrong and it saves us one strlen()
- if (messagelen > CHAT_SIZE) { // messages mustn't be too long
+ text = (char*)RFIFOP(fd,4);
+ textlen = packetlen - 4;
+
+ name = text;
+ namelen = strnlen(sd->status.name, NAME_LENGTH - 1);
+ // verify <name> part of the packet
+ if( strncmp(name, sd->status.name, namelen) || // the text must start with the speaker's name
+ name[namelen] != ' ' || name[namelen+1] != ':' || name[namelen+2] != ' ' ) // followed by ' : '
+ {
+ //Hacked message, or infamous "client desynch" issue where they pick one char while loading another.
+ ShowWarning("clif_parse_GlobalMessage: Player '%s' sent a message using an incorrect name! Forcing a relog...", sd->status.name);
+ clif_setwaitclose(fd); // Just kick them out to correct it.
+ return;
+ }
+
+ message = text + namelen + 3;
+ messagelen = textlen - namelen - 3 - 1; // this should be the message length
+ // verify <message> part of the packet
+ if( message[messagelen] != '\0' )
+ { // message must be zero-terminated
+ ShowWarning("clif_parse_GlobalMessage: Player '%s' sent an unterminated string!", sd->status.name);
+ return;
+ }
+ if( messagelen != strnlen(message, messagelen+1) )
+ { // the declared length must match real length
+ ShowWarning("clif_parse_GlobalMessage: Received malformed packet from player '%s' (length is incorrect)!", sd->status.name);
+ return;
+ }
+ if( messagelen > CHATBOX_SIZE )
+ { // messages mustn't be too long
int i;
// special case here - allow some more freedom for frost joke & dazzler
// TODO:? You could use a state flag when FrostJoke/Scream is used, and unset it once the skill triggers. [Skotlex]
- for(i = 0; i < MAX_SKILLTIMERSKILL && sd->ud.skilltimerskill[i] &&
- sd->ud.skilltimerskill[i]->skill_id != BA_FROSTJOKE &&
- sd->ud.skilltimerskill[i]->skill_id != DC_SCREAM; i++);
+ ARR_FIND( 0, MAX_SKILLTIMERSKILL, i, sd->ud.skilltimerskill[i] == 0 || sd->ud.skilltimerskill[i]->skill_id == BA_FROSTJOKE || sd->ud.skilltimerskill[i]->skill_id == DC_SCREAM );
- if (i == MAX_SKILLTIMERSKILL || !sd->ud.skilltimerskill[i]) { // normal message, too long
- ShowWarning("clif_parse_GlobalMessage: Player '%s' sent a message too long ('%.*s')!", sd->status.name, CHAT_SIZE, message);
+ if( i == MAX_SKILLTIMERSKILL || !sd->ud.skilltimerskill[i])
+ { // normal message, too long
+ ShowWarning("clif_parse_GlobalMessage: Player '%s' sent a message too long ('%.*s')!", sd->status.name, CHATBOX_SIZE, message);
return;
}
- if (messagelen > 255) { // frost joke/dazzler, but still too long
+ if( messagelen > 255 )
+ { // frost joke/dazzler, but still too long
ShowWarning("clif_parse_GlobalMessage: Player '%s' sent a message too long ('%.*s')!", sd->status.name, 255, message);
return;
}
}
- if (message[messagelen-1] != '\0') { // message must be zero-terminated
- ShowWarning("clif_parse_GlobalMessage: Player '%s' sent an unterminated string!", sd->status.name);
- return;
- }
- namelen = strnlen(sd->status.name, NAME_LENGTH - 1);
- if (strncmp(message, sd->status.name, namelen) || // the name has to match the speaker's name
- message[namelen] != ' ' || message[namelen+1] != ':' || message[namelen+2] != ' ') // completely, not just the prefix
- {
- //Hacked message, or infamous "client desynch" issue where they pick one char while loading another.
- clif_setwaitclose(fd); // Just kick them out to correct it.
- ShowWarning("clif_parse_GlobalMessage: Player '%.*s' sent a message using an incorrect name ('%s')! Forcing a relog...", namelen, sd->status.name, message);
- return;
- }
-
- if (is_atcommand(fd, sd, message) != AtCommand_None || is_charcommand(fd, sd, message) != CharCommand_None)
+ if( is_atcommand(fd, sd, text) != AtCommand_None || is_charcommand(fd, sd, text) != CharCommand_None )
return;
- if (sd->sc.count &&
- (sd->sc.data[SC_BERSERK].timer != -1 ||
- (sd->sc.data[SC_NOCHAT].timer != -1 && sd->sc.data[SC_NOCHAT].val1&MANNER_NOCHAT)))
+ if( sd->sc.data[SC_BERSERK].timer != -1 || (sd->sc.data[SC_NOCHAT].timer != -1 && sd->sc.data[SC_NOCHAT].val1&MANNER_NOCHAT) )
return;
- if (battle_config.min_chat_delay)
+ if( battle_config.min_chat_delay )
{ //[Skotlex]
if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0)
return;
@@ -8451,11 +8465,11 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
}
// send message to others
- WFIFOHEAD(fd, messagelen + 8);
+ WFIFOHEAD(fd, 8 + textlen);
WFIFOW(fd,0) = 0x8d;
- WFIFOW(fd,2) = messagelen + 8;
+ WFIFOW(fd,2) = 8 + textlen;
WFIFOL(fd,4) = sd->bl.id;
- memcpy(WFIFOP(fd,8), message, messagelen);
+ safestrncpy((char*)WFIFOP(fd,8), text, textlen);
clif_send(WFIFOP(fd,0), WFIFOW(fd,2), &sd->bl, sd->chatID ? CHAT_WOS : AREA_CHAT_WOC);
// send back message to the speaker
@@ -8465,37 +8479,38 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
#ifdef PCRE_SUPPORT
// trigger listening mobs/npcs
- map_foreachinrange(npc_chat_sub, &sd->bl, AREA_SIZE, BL_NPC, message, strlen(message), &sd->bl);
- map_foreachinrange(mob_chat_sub, &sd->bl, AREA_SIZE, BL_MOB, message, strlen(message), &sd->bl);
+ map_foreachinrange(npc_chat_sub, &sd->bl, AREA_SIZE, BL_NPC, text, textlen, &sd->bl);
+ map_foreachinrange(mob_chat_sub, &sd->bl, AREA_SIZE, BL_MOB, text, textlen, &sd->bl);
#endif
// check for special supernovice phrase
- if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) {
+ if( (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE )
+ {
int next = pc_nextbaseexp(sd);
- if (next > 0 && (sd->status.base_exp * 1000 / next)% 100 == 0) { // 0%, 10%, 20%, ...
+ if( next > 0 && (sd->status.base_exp * 1000 / next)% 100 == 0 ) { // 0%, 10%, 20%, ...
switch (sd->state.snovice_call_flag) {
case 0:
- if (strstr(message, msg_txt(504))) // "Guardian Angel, can you hear my voice? ^^;"
+ if( strstr(message, msg_txt(504)) ) // "Guardian Angel, can you hear my voice? ^^;"
sd->state.snovice_call_flag++;
break;
case 1: {
char buf[256];
sprintf(buf, msg_txt(505), sd->status.name);
- if (strstr(message, buf)) // "My name is %s, and I'm a Super Novice~"
+ if( strstr(message, buf) ) // "My name is %s, and I'm a Super Novice~"
sd->state.snovice_call_flag++;
}
break;
case 2:
- if (strstr(message, msg_txt(506))) // "Please help me~ T.T"
+ if( strstr(message, msg_txt(506)) ) // "Please help me~ T.T"
sd->state.snovice_call_flag++;
break;
case 3:
- if (skillnotok(MO_EXPLOSIONSPIRITS,sd))
- break; //Do not override the noskill mapflag. [Skotlex]
- clif_skill_nodamage(&sd->bl,&sd->bl,MO_EXPLOSIONSPIRITS,-1,
- sc_start(&sd->bl,SkillStatusChangeTable(MO_EXPLOSIONSPIRITS),100,
+ if( skillnotok(MO_EXPLOSIONSPIRITS,sd) )
+ break; //Do not override the noskill mapflag. [Skotlex]
+ clif_skill_nodamage(&sd->bl,&sd->bl,MO_EXPLOSIONSPIRITS,-1,
+ sc_start(&sd->bl,SkillStatusChangeTable(MO_EXPLOSIONSPIRITS),100,
17,skill_get_time(MO_EXPLOSIONSPIRITS,1))); //Lv17-> +50 critical (noted by Poki) [Skotlex]
- sd->state.snovice_call_flag = 0;
+ sd->state.snovice_call_flag = 0;
break;
}
}