Tutorial *133*

OK. I'm fairly new to quake engine coding, but, like they say necessity is the mother of invention. I help admin a number of game servers in New Zealand, and one of the things the GamesMaster has been asking me about for some time is banning multiple IP addresses. (We have a few troubled souls that like to wreck the game for other people). Well, I finally got around to looking at the issue and doing something about it.
My aim was to upgrade the banning function in Quake, and enable the banning of multiple IP address's in the form of a "banned.txt" file. While doing this I've been a bit lazy and kept things simple. I've effectively removed the old console ban command, and the ability to ban whole subnets. For those that want to take this further, you may want to have the ban command append an address to the ban file (not hard to do), anyway, on with the code.

All of the changes to be made are in the net_dgrm.c file.

First off, replace the code for the NET_Ban_f command with the following. We're removing the argument checking switch, and telling whoever uses the ban command to look to the "banfile" command instead.



void NET_Ban_f (void)

{

	char addrStr [32];

	char maskStr [32];

	void (*print) (char *fmt, ...);



	if (cmd_source == src_command)

	{

		if (!sv.active)

		{

			Cmd_ForwardToServer ();

			return;

		}

		print = Con_Printf;

	}

	else

	{

		if (pr_global_struct->deathmatch && !host_client->privileged)

			return;

		print = SV_ClientPrintf;

	}

	print("Banning from Banfile\n");

	print("Use \"banfile\" to see banned addresses\n");

}

Alright, so far so good. Right, now at the end of the above command that you've just edited, you'll see an "#endif", under that line paste the code below.





//-------------------Banfile command by Azuth, prints banfile entries--------------------

void Banfile_f (void)

{



	FILE *inf;



	char banline[32];

	void (*print) (char *fmt, ...);



	print = Con_Printf;



	inf = fopen ("banned.txt", "r");

	if (inf == NULL) print("Can't Open File\n");



	else

	{

	while (fgets(banline, 31, inf) != NULL)

	{

		print(banline);

	}

	}

	close(inf);

}



//---------------------------------------------------------------------------------------

This command is a real simple one, it opens the file "banned.txt" and prints the contents to the console. I've kept string lengths the same as the original ban command, this is not strictly necessary for this part of the code, but worth doing incase you start fooling around some.
Now we need to let quake know that "banfile" is a command that can be used from the console, look for the code below and add the line with the comment.



Cmd_AddCommand ("test", Test_f);

Cmd_AddCommand ("test2", Test2_f);



Cmd_AddCommand ("banfile", Banfile_f); // Add this line, to access the banfile command.

Okay. We're on the home stretch, now all we need to do is tell quake to look at the "banned.txt" file when it checks to see who's banned. (The bit that actually does the work). Find the following code and add the lines between the comments.



static qsocket_t *_Datagram_CheckNewConnections (void)

{

	struct qsockaddr clientaddr;

	struct qsockaddr newaddr;

	int newsock;

	int acceptsock;

	qsocket_t *sock;

	qsocket_t *s;

	int len;

	int command;

	int control;

	int ret;



	//----------Added for Azuth's banfile checking

	FILE *inf;

	char banline[32];

	void (*print) (char *fmt, ...);

	print = Con_Printf;

	//----------

Okay we've defined what we need to to put the actual ban checking code in, onwards to the grand finale. Find the BAN_TEST command, it should look like the one below.



#ifdef BAN_TEST

// check for a ban

if (clientaddr.sa_family == AF_INET)

{

	unsigned long testAddr;

	testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr;

	if ((testAddr & banMask) == banAddr)

	{

		SZ_Clear(&net_message);

		// save space for the header, filled in later

		MSG_WriteLong(&net_message, 0);

		MSG_WriteByte(&net_message, CCREP_REJECT);

		MSG_WriteString(&net_message, "You have been banned.\n");

		*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));

		dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);

		SZ_Clear(&net_message);

		return NULL;

	}

}

#endif

Replace the above block of code with the following.



#ifdef BAN_TEST

// check for a ban using Azuth's banfile system



if (clientaddr.sa_family == AF_INET)



inf = fopen ("banned.txt", "r"); //open banned.txt for ban checking

if (inf == NULL)

{

	print("Can't Open File, creating banned.txt quake dir\n"); //Returns an error if file cannot be openned

	inf = fopen ("banned.txt", "w");

	close(inf);

	}

	{

	unsigned long testAddr;

	testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr;



	while (fgets(banline, 31, inf) != NULL)  // reads banned.txt line by line

	{

		banAddr = inet_addr(banline); //Assigns the line to banAddr using the predefined structure



		if ((testAddr & banMask) == banAddr)

		{

			SZ_Clear(&net_message);

			// save space for the header, filled in later

			MSG_WriteLong(&net_message, 0);

			MSG_WriteByte(&net_message, CCREP_REJECT);

			MSG_WriteString(&net_message, "You have been banned.\n");

			*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));

			dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);

			SZ_Clear(&net_message);

			return NULL;

		}

		close(inf);

	}

}

#endif

Well, that's all she wrote. Compile it and place your banned IP's in a "banned.txt" file in your quake directory. A "banned.txt" file gets created the first time someone tries to connect to the server if it isn't there. I couldn't say how many entries it would take to noticeably slow down the client connection process, but it would be quite a few.

Where to go from here? Take a look at the "file access from QC" tutorial from FrikaC, maybe you'll want to have admins ban people remotely via the admin.qc impulses.

Take care, and please feel free to email me comments or questions.



 
Not logged in
Sign up
Login:
Passwd: