phantasia4/
phantasia4/conf/
phantasia4/phantasia/bin/
phantasia4/phantasia/src/utilities/
phantasia4/public_html/cgi-bin/
/*
 * 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;
}