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. 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. 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. |