/*
* hack.c Routines to detect/prevent hacks
*/
#include "include.h"
/************************************************************************
/
/ FUNCTION NAME: Do_caught_hack(c)
/
/ FUNCTION: return a char specifying player type
/
/ AUTHOR: Brian Kelly, 01/06/01
/
/ ARGUMENTS:
/ struct client_t c - pointer to the main client strcture
/
/ RETURN VALUE:
/ char - character the describes the character
/
/ MODULES CALLED: strcpy()
/
/ DESCRIPTION:
/ Return a string describing the player type.
/ King, council, valar, supercedes other types.
/ The first character of the string is '*' if the player
/ has a crown.
/ If 'shortflag' is TRUE, return a 3 character string.
/
*************************************************************************/
Do_caught_hack(struct client_t *c, int theReason)
{
int hackCount, expiredAttempts;
struct account_t theAccount;
struct account_mod_t theAccountMod;
struct network_t theNetwork;
struct tag_t theTag;
char string_buffer[SZ_LINE], error_msg[SZ_ERROR_MESSAGE];
time_t timeNow;
bool rejectFlag;
/* ban times in seconds- one hour, one day, one week, one month,
four months, one year */
int tagTimes[7] = {0, 3600, 86400, 604800, 2592000, 9676800, 31536000};
timeNow = time(NULL);
rejectFlag = FALSE;
/* lock the hack lock to prevent these stats from updating by others */
Do_lock_mutex(&c->realm->hack_lock);
theAccount.hackCount = 0;
/* find past triggers, start with the character, if it exists */
/* skip the character, if it exists, it's going to be killed */
if (c->accountLoaded) {
/* load the account - there may be a hack since last load */
if (Do_look_account(c, c->lcaccount, &theAccount)) {
/* if previous offenses, lower count by 1 per month since */
if (theAccount.hackCount) {
theAccount.hackCount -= (timeNow - theAccount.lastHack) /
2592000;
if (theAccount.hackCount < 0) {
theAccount.hackCount = 0;
}
}
}
}
/* now get the information on the connection - it may not exist! */
if (Do_look_network(c, c->network, &theNetwork)) {
/* if previous offenses, lower count by 1 per month since */
expiredAttempts = (timeNow - theNetwork.lastHack) / 2592000;
if (theNetwork.hackCount) {
theNetwork.hackCount -= expiredAttempts;
if (theNetwork.hackCount < 0) {
theNetwork.hackCount = 0;
}
}
}
else {
strcpy(theNetwork.address, c->network);
theNetwork.hackCount = 0;
}
/* find the highest number of hack and rejections attempts */
if (theAccount.hackCount > theNetwork.hackCount) {
hackCount = theAccount.hackCount;
}
else {
hackCount = theNetwork.hackCount;
}
/* increment the hack count */
++hackCount;
/* max ban is a year */
if (hackCount > 6) {
hackCount = 6;
}
/* save the account info */
if (c->accountLoaded) {
Do_clear_account_mod(&theAccountMod);
theAccountMod.hack = TRUE;
theAccountMod.hackCount = hackCount;
Do_modify_account(c, c->lcaccount, NULL, &theAccountMod);
}
/* update the address info */
theNetwork.hackCount = hackCount;
theNetwork.lastHack = timeNow;
if (theNetwork.expires < timeNow + hackCount * 2592000) {
theNetwork.expires = timeNow + hackCount * 2592000;
}
Do_update_network(c, &theNetwork);
/* prepare a tag for the account and address */
theTag.type = T_BAN;
theTag.validUntil = timeNow + tagTimes[hackCount];
theTag.affectNetwork = FALSE;
Do_get_hack_string(theReason, theTag.description);
/* send it */
Do_tag_self(c, &theTag);
Do_unlock_mutex(&c->realm->hack_lock);
return;
}
/************************************************************************
/
/ FUNCTION NAME: int Do_look_network(struct client_t *c, char *networkName, struct network_t *theNetwork)
/
/ FUNCTION: return a char specifying player type
/
/ AUTHOR: Brian Kelly, 1/3/01
/
/ ARGUMENTS:
/ struct client_t c - pointer to the main client strcture
/
/ RETURN VALUE:
/ char - character the describes the character
/
/ MODULES CALLED: strcpy()
/
/ DESCRIPTION:
/ Return a string describing the player type.
/ King, council, valar, supercedes other types.
/ The first character of the string is '*' if the player
/ has a crown.
/ If 'shortflag' is TRUE, return a 3 character string.
/
*************************************************************************/
int Do_look_network(struct client_t *c, char *networkName, struct network_t *theNetwork)
{
char string_buffer[SZ_LINE];
FILE *network_file;
/* open the network file */
errno = 0;
Do_lock_mutex(&c->realm->network_file_lock);
if ((network_file=fopen(NETWORK_FILE, "r")) == NULL) {
Do_unlock_mutex(&c->realm->network_file_lock);
sprintf(string_buffer,
"[%s] fopen of %s failed in Do_look_network: %s.\n",
c->connection_id, NETWORK_FILE, strerror(errno));
Do_log_error(string_buffer);
return FALSE;
}
/* run through the network entries */
while (fread((void *)theNetwork, SZ_NETWORK, 1, network_file) == 1) {
/* if this is the network */
if (strcmp(networkName, theNetwork->address) == 0) {
/* return with the good news */
fclose(network_file);
Do_unlock_mutex(&c->realm->network_file_lock);
return TRUE;
}
}
/* close down and return a negative */
fclose(network_file);
Do_unlock_mutex(&c->realm->network_file_lock);
theNetwork->hackCount = 0;
theNetwork->muteCount = 0;
return FALSE;
}
/************************************************************************
/
/ FUNCTION NAME: Do_update_network(struct client_t *c, struct network_t *theNetwork)
/
/ FUNCTION: find location in player file of given name
/
/ AUTHOR: Brian Kelly, 01/06/01
/
/ ARGUMENTS:
/ char *name - name of character to look for
/ struct player *playerp - pointer of structure to fill
/
/ RETURN VALUE: location of player if found, -1 otherwise
/
/ MODULES CALLED: fread(), fseek(), strcmp()
/
/ GLOBAL INPUTS: Wizard, *Playersfp
/
/ GLOBAL OUTPUTS: none
/
/ DESCRIPTION:
/ Search the player file for the player of the given name.
/ If player is found, fill structure with player data.
/
*************************************************************************/
Do_update_network(struct client_t *c, struct network_t *theNetwork)
{
struct network_t readNetwork;
FILE *network_file, *temp_file;
char error_msg[SZ_ERROR_MESSAGE];
time_t timeNow;
bool found_flag;
Do_lock_mutex(&c->realm->network_file_lock);
/* get the time now */
timeNow = time(NULL);
found_flag = FALSE;
/* open a temp file to transfer records to */
errno = 0;
if ((temp_file=fopen(TEMP_NETWORK_FILE, "w")) == NULL) {
Do_unlock_mutex(&c->realm->network_file_lock);
sprintf(error_msg,
"[%s] fopen of %s failed in Do_update_network: %s.\n",
c->connection_id, TEMP_NETWORK_FILE, strerror(errno));
Do_log_error(error_msg);
return FALSE;
}
errno = 0;
if ((network_file=fopen(NETWORK_FILE, "r")) == NULL) {
Do_unlock_mutex(&c->realm->network_file_lock);
sprintf(error_msg,
"[%s] fopen of %s failed in Do_update_network: %s\n",
c->connection_id, NETWORK_FILE, strerror(errno));
Do_log_error(error_msg);
}
else {
/* run through the network entries */
while (fread((void *)&readNetwork, SZ_NETWORK, 1, network_file) == 1) {
/* if this is the network */
if (strcmp(readNetwork.address, theNetwork->address) == 0) {
found_flag = TRUE;
/* write the passed strcture */
if (fwrite((void *)theNetwork, SZ_NETWORK, 1, temp_file) != 1) {
fclose(network_file);
fclose(temp_file);
remove(TEMP_NETWORK_FILE);
Do_unlock_mutex(&c->realm->network_file_lock);
sprintf(error_msg,
"[%s] fwrite of %s failed in Do_update_network: %s.\n",
c->connection_id, TEMP_NETWORK_FILE, strerror(errno));
Do_log_error(error_msg);
return FALSE;
}
continue;
}
else {
/* see if this network is still valid */
if (timeNow > readNetwork.expires) {
/* log the deletion of the network */
sprintf(error_msg,
"[%s] Deleted hack info of the network %s.\n",
c->connection_id, readNetwork.address);
Do_log(CONNECTION_LOG, error_msg);
/* don't save this network */
continue;
}
}
/* write the network to the temp file */
if (fwrite((void *)&readNetwork, SZ_NETWORK, 1, temp_file) != 1) {
fclose(network_file);
fclose(temp_file);
remove(TEMP_NETWORK_FILE);
Do_unlock_mutex(&c->realm->network_file_lock);
sprintf(error_msg,
"[%s] fwrite of %s failed in Do_update_network(2): %s.\n",
c->connection_id, TEMP_NETWORK_FILE, strerror(errno));
Do_log_error(error_msg);
return FALSE;
}
}
fclose(network_file);
}
/* if we've not written in our network, we make a new entry */
if (!found_flag) {
/* write the new network to the temp file */
if (fwrite((void *)theNetwork, SZ_NETWORK, 1, temp_file) != 1) {
fclose(temp_file);
remove(TEMP_NETWORK_FILE);
Do_unlock_mutex(&c->realm->network_file_lock);
sprintf(error_msg,
"[%s] fwrite of %s failed in Do_update_network(3): %s.\n",
c->connection_id, TEMP_NETWORK_FILE, strerror(errno));
Do_log_error(error_msg);
return FALSE;
}
}
/* close the two files */
fclose(temp_file);
/* delete the old character record */
remove(NETWORK_FILE);
/* replace it with the temporary file */
rename(TEMP_NETWORK_FILE, NETWORK_FILE);
Do_unlock_mutex(&c->realm->network_file_lock);
return TRUE;
}
/************************************************************************
/
/ FUNCTION NAME: Do_tally_ip(struct client_t *c, bool connection, short badPasswords);
/
/ FUNCTION: find location in player file of given name
/
/ AUTHOR: Brian Kelly, 01/06/01
/
/ ARGUMENTS:
/ char *name - name of character to look for
/ struct player *playerp - pointer of structure to fill
/
/ RETURN VALUE: location of player if found, -1 otherwise
/
/ MODULES CALLED: fread(), fseek(), strcmp()
/
/ GLOBAL INPUTS: Wizard, *Playersfp
/
/ GLOBAL OUTPUTS: none
/
/ DESCRIPTION:
/ Search the player file for the player of the given name.
/ If player is found, fill structure with player data.
/
*************************************************************************/
Do_tally_ip(struct client_t *c, bool connection, short badPasswords)
{
struct connection_t *connection_ptr, **connection_ptr_ptr;
time_t timeNow;
Do_lock_mutex(&c->realm->connections_lock);
timeNow = time(NULL);
connection_ptr_ptr = &c->realm->connections;
/* run through the list looking for previous entries */
while (*connection_ptr_ptr != NULL) {
connection_ptr = *connection_ptr_ptr;
/* is this the network we're talking about? */
if (strcmp(connection_ptr->theAddress, c->network) == 0) {
/* if we're adding a connection */
if (connection) {
/* drop out expired connections */
Do_drop_expired(connection_ptr->connections,
&connection_ptr->connectionCount, timeNow - 900);
/* see if this connection will be too many */
if (connection_ptr->connectionCount > 19) {
connection_ptr->connectionCount = 0;
Do_unlock_mutex(&c->realm->connections_lock);
Do_caught_hack(c, H_CONNECTIONS);
return;
}
connection_ptr->connections[connection_ptr->connectionCount++]
= timeNow;
if (connection_ptr->eraseAt < timeNow + 600) {
connection_ptr->eraseAt = timeNow + 600;
}
}
if (badPasswords) {
/* drop out expired missed passwords */
Do_drop_expired(connection_ptr->badPasswords,
&connection_ptr->badPasswordCount, timeNow - 1800);
/* see if these additions will be too many */
if (connection_ptr->badPasswordCount + badPasswords > 9) {
connection_ptr->badPasswordCount = 0;
Do_unlock_mutex(&c->realm->connections_lock);
Do_caught_hack(c, H_PASSWORDS);
return;
}
while (badPasswords > 0) {
connection_ptr->badPasswords[
connection_ptr->badPasswordCount++] = timeNow;
--badPasswords;
}
if (connection_ptr->eraseAt < timeNow + 1800) {
connection_ptr->eraseAt = timeNow + 1800;
}
}
Do_unlock_mutex(&c->realm->connections_lock);
return;
}
else {
/* see if this address should be deleted */
if (connection_ptr->eraseAt < timeNow) {
/* delete the current entry and point to the next */
*connection_ptr_ptr = connection_ptr->next;
free((void *)connection_ptr);
}
else {
/* increment to the next pointer */
connection_ptr_ptr = &(*connection_ptr_ptr)->next;
}
}
}
/* no connection record for us, so make our own */
connection_ptr = (struct connection_t *) Do_malloc(SZ_CONNECTION);
strcpy(connection_ptr->theAddress, c->network);
connection_ptr->connectionCount = 0;
connection_ptr->badPasswordCount = 0;
/* if we're adding a connection */
if (connection) {
connection_ptr->connections[0] = timeNow;
connection_ptr->connectionCount = 1;
connection_ptr->eraseAt = timeNow + 600;
}
if (badPasswords) {
while (badPasswords > 0) {
connection_ptr->badPasswords[
connection_ptr->badPasswordCount++] = timeNow;
--badPasswords;
}
connection_ptr->eraseAt = timeNow + 1800;
}
/* put the new connection record in place */
connection_ptr->next = c->realm->connections;
c->realm->connections = connection_ptr;
Do_unlock_mutex(&c->realm->connections_lock);
return;
}
/************************************************************************
/
/ FUNCTION NAME: Do_drop_expired(int theArray[], int *elements, int eraseTime)
/
/ FUNCTION: find location in player file of given name
/
/ AUTHOR: Brian Kelly, 01/06/01
/
/ ARGUMENTS:
/ char *name - name of character to look for
/ struct player *playerp - pointer of structure to fill
/
/ RETURN VALUE: location of player if found, -1 otherwise
/
/ MODULES CALLED: fread(), fseek(), strcmp()
/
/ GLOBAL INPUTS: Wizard, *Playersfp
/
/ GLOBAL OUTPUTS: none
/
/ DESCRIPTION:
/ Search the player file for the player of the given name.
/ If player is found, fill structure with player data.
/
*************************************************************************/
Do_drop_expired(int theArray[], int *elements, int dropBefore)
{
int i, j;
/* the lowest elements will be bad */
if (theArray[0] > dropBefore) {
return;
}
/* if they're all bad, we don't need to move anything */
if (theArray[*elements - 1] < dropBefore) {
*elements = 0;
return;
}
/* run through the elements to find non-expired time */
for (i = 1; i < *elements; i++) {
if (theArray[i] > dropBefore) {
break;
}
}
/* move the times down */
for (j = i; j < *elements; j++) {
theArray[j - i] = theArray[j];
}
*elements -= i;
return;
}
/************************************************************************
/
/ FUNCTION NAME: Do_get_hack_string(int number, char *theString);
/
/ FUNCTION: return a char specifying player type
/
/ AUTHOR: Brian Kelly, 01/09/01
/
/ ARGUMENTS:
/ struct client_t c - pointer to the main client strcture
/
/ RETURN VALUE:
/ char - character the describes the character
/
/ MODULES CALLED: strcpy()
/
/ DESCRIPTION:
/ Return a string describing the player type.
/ King, council, valar, supercedes other types.
/ The first character of the string is '*' if the player
/ has a crown.
/ If 'shortflag' is TRUE, return a 3 character string.
/
*************************************************************************/
Do_get_hack_string(int number, char *theString)
{
switch(number) {
case H_SYSTEM:
strcpy(theString, "hacking the system");
break;
case H_PASSWORDS:
strcpy(theString, "hacking passwords");
break;
case H_CONNECTIONS:
strcpy(theString, "making excessive connections");
break;
case H_KILLING:
strcpy(theString, "killing characters");
break;
case H_PROFANITY:
strcpy(theString, "using profanity on channel 1");
break;
case H_DISRESPECTFUL:
strcpy(theString, "disrespecting the wizards");
break;
case H_FLOOD:
strcpy(theString, "sending too many long messages");
break;
case H_SPAM:
strcpy(theString, "sending too many messages at once");
break;
case H_WHIM:
strcpy(theString, "the wizard's whim");
break;
default:
strcpy(theString, "listening to Neil Diamond");
break;
}
return;
}
/************************************************************************
/
/ FUNCTION NAME: Do_caught_spam(c, int theReason)
/
/ FUNCTION: return a char specifying player type
/
/ AUTHOR: Brian Kelly, 01/06/01
/
/ ARGUMENTS:
/ struct client_t c - pointer to the main client strcture
/
/ RETURN VALUE:
/ char - character the describes the character
/
/ MODULES CALLED: strcpy()
/
/ DESCRIPTION:
/ Return a string describing the player type.
/ King, council, valar, supercedes other types.
/ The first character of the string is '*' if the player
/ has a crown.
/ If 'shortflag' is TRUE, return a 3 character string.
/
*************************************************************************/
Do_caught_spam(struct client_t *c, int theReason)
{
struct account_t theAccount;
struct account_mod_t theAccountMod;
struct tag_t theTag;
char string_buffer[SZ_LINE], error_msg[SZ_ERROR_MESSAGE];
time_t timeNow;
bool banFlag;
/* mute times; 1 min, 5 min, 20 min, 1 hour, 4 hours, 1 day, 3 days, 1 week */
int muteTimes[8] = {60, 300, 1200, 7200, 28800, 86400, 259200, 604800};
timeNow = time(NULL);
banFlag = FALSE;
/* lock the hack lock to prevent these stats from updating by others */
Do_lock_mutex(&c->realm->hack_lock);
/* find past triggers, start with the character, if it exists */
/* skip the character, if it exists, it's going to be killed */
if (Do_look_account(c, c->lcaccount, &theAccount)) {
/* if previous offenses, lower count by 1 per two weeks since */
if (theAccount.muteCount) {
theAccount.muteCount -= (timeNow - theAccount.lastMute) /
1296000;
if (theAccount.muteCount < 0) {
theAccount.muteCount = 0;
}
}
}
/* find the highest number of mutes */
if (theAccount.muteCount > c->player.muteCount) {
c->player.muteCount = theAccount.muteCount;
}
/* There's an overflow with the mute count somewhere -- EH */
if (c->player.muteCount > 10 || c->player.muteCount < 0) {
c->player.muteCount = 1;
}
/* increment the mute count */
++c->player.muteCount;
/* max mute punishment is one week */
if (c->player.muteCount > 7) {
c->player.muteCount = 7;
banFlag = TRUE;
}
/* save the account info */
if (c->accountLoaded) {
Do_clear_account_mod(&theAccountMod);
theAccountMod.mute = TRUE;
theAccountMod.muteCount = c->player.muteCount;
Do_modify_account(c, c->lcaccount, NULL, &theAccountMod);
}
/* prepare a tag */
theTag.type = T_MUTE;
theTag.validUntil = timeNow + muteTimes[c->player.muteCount];
theTag.affectNetwork = FALSE;
theTag.contagious = FALSE;
Do_get_hack_string(theReason, theTag.description);
/* send it */
Do_tag_self(c, &theTag);
Do_unlock_mutex(&c->realm->hack_lock);
sprintf(string_buffer, "%s has been muted by the server for %s.\n", c->modifiedName, theTag.description);
Do_broadcast(c, string_buffer);
if (banFlag) {
Do_caught_hack(c, theReason);
}
return;
}