536 lines
16 KiB
C
536 lines
16 KiB
C
/*==============================================================================
|
|
The Weapons Factory -
|
|
IP Banning Code
|
|
Original code by Red Barchetta <paradox@pegasus.rutgers.edu>
|
|
Modified by Gregg Reno
|
|
==============================================================================*/
|
|
#include "g_local.h"
|
|
#define BAN_FILE "listip.cfg"
|
|
#define IPSEP '.'
|
|
|
|
/* Comments
|
|
I didn't add anything from newer CTF versions. My ban commands are "ban"
|
|
and "unban" (with "bans" showing a list). It would be easy enough to
|
|
rename them, though, if you rewrite them to behave like Zoid's.
|
|
|
|
Here comes... I'm just going to paste the pieces of code into the message,
|
|
since I need to cut up files to extract it. There are definitely
|
|
better/shorter ways to do some of this stuff... it just isn't worth the
|
|
time to make it efficient, since it is only called when you add an IP to
|
|
the list.
|
|
|
|
Also, I don't know if WF has an admin system (I would magine it does). If
|
|
not, just rip out the admin checks in the commands (Adm_*). Also, you
|
|
obviosly need to put calls to Adm_* in g_cmds.c (and g_svcmds.c if you want).
|
|
|
|
Have fun... don't distribute this, and please send me whatever
|
|
changes/improvements you make.
|
|
|
|
|
|
// I use this to allow the file name to be changed.
|
|
// put it in with the other cvar inits at game start if you want it
|
|
// (you need to declare ban_file and gamedir to be extern cvar_t *'s
|
|
ban_file = gi.cvar("ban_file", "", CVAR_USERINFO);
|
|
|
|
|
|
*/
|
|
|
|
int isspace(char c)
|
|
{
|
|
if (c == ' ' || c == 10 || c == 13) return 1;
|
|
else return 0;
|
|
}
|
|
|
|
//The filter ban determines how the ip addresses are used. If
|
|
//the filterban cvar = 1 (normal), then any player matching an
|
|
//item in the ban list will be disallowed. If it is set to 0, then
|
|
//all player that do NOT have an entry in the banlist will be
|
|
//disallowed. This function simply changes the return code
|
|
//based on the filter ban
|
|
int CheckFilterBan(int retval)
|
|
{
|
|
if ((int)filterban->value == 0)
|
|
{
|
|
if (retval == 0) //swap return values
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
else
|
|
return retval;
|
|
}
|
|
|
|
// check to see if ip is on the banlist
|
|
int IsBanned(char *ip)
|
|
{
|
|
ban_t *tmp = banlist;
|
|
//gi.dprintf("Testing ban of %s\n",ip);
|
|
|
|
while (tmp)
|
|
{
|
|
if (tmp->subnet)
|
|
{
|
|
if (!strncmp(tmp->ip, ip, tmp->subnet))
|
|
return CheckFilterBan(1);
|
|
}
|
|
else if (!strcmp(tmp->ip, ip))
|
|
{
|
|
return CheckFilterBan(1);
|
|
}
|
|
|
|
// "else"
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
// if we're here, ip is not on the banlist
|
|
return CheckFilterBan(0);
|
|
}
|
|
|
|
// check for properly-formatted IP address
|
|
// NOTE you may need to hack this up to do Zoid-like subnet banning...
|
|
int IsValidIP(char *ip) {
|
|
char *term; // don't need to allocate, for whatever reason...
|
|
// in fact, crashes if allocated and then later free()'d
|
|
|
|
char *dot1, *dot2, *dot3, *end;
|
|
|
|
if (!((dot1 = strchr(ip, IPSEP)) // check for first dot
|
|
&& (dot2 = strchr(dot1 + 1, IPSEP)) // check for second dot
|
|
&& (dot3 = strchr(dot2 + 1, IPSEP)) // check for third dot
|
|
&& !strchr(dot3 + 1, IPSEP))) { // shouldn't be more than 3 dots
|
|
return 0;
|
|
}
|
|
|
|
// find the end of the address (this is the address of the NULL char)
|
|
end = ip + strlen(ip);
|
|
|
|
// check for more than 3 chars in any field
|
|
// - 1's are to account for the 2 dots
|
|
if ((dot1 - ip > 3) || (dot2 - dot1 - 1 > 3)
|
|
|| (dot3 - dot2 - 1 > 3) || (end - dot3 - 1 > 3)) {
|
|
return 0;
|
|
}
|
|
|
|
// now check for fewer than 1 char in any field
|
|
if ((dot1 - ip < 1) || (dot2 - dot1 - 1 < 1)
|
|
|| (dot3 - dot2 - 1 < 1) || (end - dot3 - 1 < 1)) {
|
|
return 0;
|
|
}
|
|
|
|
// make sure fields contain only digits
|
|
if (strtol(ip, &term, 10) > 255 || *term != '.') {
|
|
return 0;
|
|
}
|
|
if (strtol(dot1 + 1, &term, 10) > 255 || *term != '.') {
|
|
return 0;
|
|
}
|
|
if (strtol(dot2 + 1, &term, 10) > 255 || *term != '.') {
|
|
return 0;
|
|
}
|
|
if (strtol(dot3 + 1, &term, 10) > 255 || *term != (char) NULL) {
|
|
return 0;
|
|
}
|
|
|
|
// IP address is good
|
|
return 1;
|
|
}
|
|
|
|
// read in the banlist
|
|
// called
|
|
void ReadBans() {
|
|
FILE *banfile;
|
|
|
|
ban_t *tmp;
|
|
|
|
int len;
|
|
int num3;
|
|
char *dot3;
|
|
char *bufptr;
|
|
char *filename;
|
|
|
|
char buf[81];
|
|
|
|
// need to tack the "game" onto the filename...
|
|
// if (banfile = fopen(filename = va("%s/%s", *(gamedir->string)
|
|
// ? gamedir->string : "baseq2",
|
|
// ban_file->string[0] ? ban_file->string : BAN_FILE), "r")) {
|
|
if (banfile = fopen(filename = va("%s/%s", *(gamedir->string)
|
|
? gamedir->string : "baseq2",BAN_FILE), "r")) {
|
|
// read a line
|
|
while (fgets(buf, 80, banfile)) {
|
|
// don't consider the '\n' that may be in buf[]
|
|
bufptr = buf;
|
|
while (!isspace(*bufptr))
|
|
bufptr++;
|
|
*bufptr = (char) NULL;
|
|
|
|
if ((len = strlen(buf)) <= MAX_IP_LENGTH
|
|
&& IsValidIP(buf) && !IsBanned(buf)) {
|
|
// add to head of list
|
|
tmp = banlist;
|
|
banlist = (ban_t *) malloc(sizeof(ban_t));
|
|
strcpy(banlist->ip, buf);
|
|
banlist->next = tmp;
|
|
|
|
// check for subnet ban
|
|
dot3 = strrchr(buf, IPSEP);
|
|
if (!(num3 = (int) strtol(dot3 + 1, (char **) NULL, 10)))
|
|
// this is 1 + INDEX of the last '.' in the IP
|
|
banlist->subnet = dot3 - buf + 1;
|
|
else
|
|
banlist->subnet = 0; // subnet is *not* banned
|
|
|
|
gi.dprintf("Added %s to the banlist.\n",
|
|
banlist->ip);
|
|
}
|
|
else {
|
|
gi.dprintf("Invalid or already-banned IP \"%s\" read from file \"%s\".\n",
|
|
buf, filename);
|
|
}
|
|
}
|
|
|
|
fclose(banfile);
|
|
}
|
|
// else nothing to do!
|
|
}
|
|
|
|
// save the banlist to disk
|
|
void WriteBans()
|
|
{
|
|
int i;
|
|
FILE *banfile;
|
|
|
|
ban_t *tmp;
|
|
|
|
// need to tack the "game" onto the filename...
|
|
// if (banfile = fopen(va("%s/%s", *(gamedir->string) ? gamedir->string
|
|
// : "baseq2",
|
|
// ban_file->string[0] ? ban_file->string : BAN_FILE), "w")) {
|
|
if (banfile = fopen(va("%s/%s", *(gamedir->string) ? gamedir->string
|
|
: "baseq2", BAN_FILE), "w")) {
|
|
tmp = banlist;
|
|
|
|
// write bans
|
|
i = 0;
|
|
while (tmp)
|
|
{
|
|
fprintf(banfile, "%s\n", tmp->ip);
|
|
tmp = tmp->next;
|
|
++i;
|
|
}
|
|
|
|
fclose(banfile);
|
|
}
|
|
// else nothing to do!
|
|
gi.dprintf("%d IP addresses saved.\n",i);
|
|
}
|
|
|
|
// test an IP against ban list
|
|
void Adm_Test(char *cmd) {
|
|
|
|
ban_t *tmp = banlist;
|
|
|
|
if (!cmd)
|
|
{
|
|
gi.dprintf("usage: sv testip <IP | player no.>\n");
|
|
return;
|
|
}
|
|
|
|
//gi.dprintf("Testing ban of %s\n",ip);
|
|
|
|
while (tmp)
|
|
{
|
|
if (tmp->subnet)
|
|
{
|
|
if (!strncmp(tmp->ip, cmd, tmp->subnet))
|
|
{
|
|
gi.dprintf("%s banned. Matched subnet %s\n",cmd,tmp->ip);
|
|
return;
|
|
}
|
|
}
|
|
else if (!strcmp(tmp->ip, cmd))
|
|
{
|
|
gi.dprintf("%s banned. Matched %s\n",cmd,tmp->ip);
|
|
return;
|
|
}
|
|
|
|
// "else"
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
gi.dprintf("IP %s is NOT banned. \n",cmd);
|
|
|
|
|
|
}
|
|
|
|
// add an IP to the ban list
|
|
void Adm_Ban(char *cmd) {
|
|
int len;
|
|
int no;
|
|
int num3;
|
|
int x = 0;
|
|
|
|
char *junk;
|
|
char *dot3;
|
|
char *tmp_ip;
|
|
// char *x;
|
|
char to_ban[MAX_IP_LENGTH + 1];
|
|
|
|
ban_t *tmp;
|
|
|
|
edict_t *cl_ent;
|
|
|
|
|
|
if (!cmd)
|
|
{
|
|
gi.dprintf("usage: sv addip <IP | player no.>\n");
|
|
return;
|
|
}
|
|
|
|
len = strlen(cmd);
|
|
//gi.dprintf("Cmd=[%s], Len = %d, minlen = %d\n",cmd, len,MIN_IP_LENGTH);
|
|
|
|
if (len > MAX_IP_LENGTH)
|
|
{
|
|
gi.dprintf("Invalid IP address.\n");
|
|
return;
|
|
}
|
|
else if (len < MIN_IP_LENGTH)
|
|
{
|
|
// check if this is a valid player number
|
|
no = (int) strtol(cmd, &junk, 10);
|
|
if ((no < 0)
|
|
|| (no >= game.maxclients)
|
|
|| (*junk != (char) NULL))
|
|
{
|
|
gi.dprintf("Invalid player number.\n");
|
|
return;
|
|
}
|
|
|
|
cl_ent = g_edicts + 1 + no; // get player
|
|
|
|
if (!cl_ent->inuse) {
|
|
gi.dprintf("Player number %d is not currently in use.\n", no);
|
|
return;
|
|
}
|
|
|
|
// get player's IP
|
|
tmp_ip = Info_ValueForKey(cl_ent->client->pers.userinfo, "ip");
|
|
|
|
// chop port number off of the IP address
|
|
while (++x <= MAX_IP_LENGTH) {
|
|
if (*(tmp_ip + x) == ':'
|
|
|| *(tmp_ip + x) == (char) NULL)
|
|
break;
|
|
}
|
|
#if 0
|
|
// chop port number off of the IP address
|
|
x = tmp_ip;
|
|
while (x) {
|
|
if (*(++x) == ':') {
|
|
*x = (char) NULL;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
// copy the IP to the ban string...
|
|
// strncpy(to_ban, tmp_ip, MAX_IP_LENGTH);
|
|
strncpy(to_ban, tmp_ip, x);
|
|
|
|
// guarantee NULL terminate...
|
|
// to_ban[MAX_IP_LENGTH] = (char) NULL;
|
|
to_ban[x + 1] = (char) NULL;
|
|
}
|
|
// this is a normal IP (we hope)
|
|
else if (IsValidIP(cmd)) {
|
|
// copy the IP to the ban string...
|
|
strncpy(to_ban, cmd, MAX_IP_LENGTH);
|
|
|
|
// guarantee NULL terminate...
|
|
to_ban[MAX_IP_LENGTH] = (char) NULL;
|
|
}
|
|
else { // invalid IP *and* player id.
|
|
gi.dprintf("Invalid IP address.\n");
|
|
return;
|
|
}
|
|
|
|
// now do the actual ban
|
|
if (IsBanned(to_ban)) {
|
|
gi.dprintf("%s is already banned.\n", to_ban);
|
|
}
|
|
else { // insert at head of list
|
|
tmp = banlist;
|
|
banlist = (ban_t *) malloc(sizeof(ban_t));
|
|
// banlist->ip = (char *) malloc(len + 1);
|
|
strcpy(banlist->ip, to_ban);
|
|
banlist->next = tmp;
|
|
|
|
// check for subnet ban
|
|
// dot3 = strrchr(gi.argv(1 + sv), IPSEP);
|
|
dot3 = strrchr(to_ban, IPSEP);
|
|
if (!(num3 = (int) strtol(dot3 + 1, (char **) NULL, 10)))
|
|
// this is 1 + INDEX of the last '.' in the IP
|
|
banlist->subnet = dot3 - to_ban + 1;
|
|
else
|
|
banlist->subnet = 0; // subnet is *not* banned
|
|
|
|
gi.dprintf("Added %s to the banlist.\n", banlist->ip);
|
|
|
|
// make it permanent if possible
|
|
WriteBans();
|
|
}
|
|
}
|
|
|
|
// remove an IP from the ban list
|
|
void Adm_Unban(char *cmd) {
|
|
int len;
|
|
int sv = 0; // changes to 1 if first arg. is "sv" (server command)
|
|
|
|
ban_t *tmp;
|
|
ban_t *tmp_prev = (ban_t *) NULL;
|
|
|
|
|
|
if (!cmd)
|
|
{
|
|
gi.dprintf("usage: sv removeip <IP>\n");
|
|
return;
|
|
}
|
|
|
|
if ((len = strlen(cmd)) > MAX_IP_LENGTH
|
|
|| len < MIN_IP_LENGTH) {
|
|
gi.dprintf("Invalid IP address.\n");
|
|
}
|
|
else {
|
|
if (IsBanned(cmd)) {
|
|
tmp = banlist;
|
|
|
|
// check first individually so we can get "previous"
|
|
if (strcmp(tmp->ip, cmd)) {
|
|
tmp_prev = tmp;
|
|
tmp = tmp->next;
|
|
|
|
while (tmp && strcmp(tmp->ip,
|
|
cmd)) {
|
|
tmp_prev = tmp;
|
|
tmp = tmp->next;
|
|
}
|
|
}
|
|
|
|
if (tmp) { // just to be sure
|
|
if (tmp_prev)
|
|
// take tmp out of the list
|
|
tmp_prev->next = tmp->next;
|
|
else //this one is the first in the list
|
|
banlist = tmp->next;
|
|
|
|
// free(tmp->ip);
|
|
free(tmp);
|
|
|
|
gi.dprintf("Removed %s from the banlist.\n",
|
|
cmd);
|
|
|
|
// make it permanent if possible
|
|
WriteBans();
|
|
}
|
|
else // subnet is banned
|
|
gi.dprintf("The entire subnet containing %s is banned. You need to remove the subnet ban to unban this IP.\n",
|
|
cmd);
|
|
}
|
|
else {
|
|
gi.dprintf("%s is not on the ban list.\n", cmd);
|
|
}
|
|
}
|
|
}
|
|
|
|
// show all bans
|
|
void Adm_Bans(char *cmd) {
|
|
int i = 0;
|
|
int sv = 0; // changes to 1 if first arg. is "sv" (server command)
|
|
|
|
ban_t *tmp;
|
|
|
|
tmp = banlist;
|
|
|
|
if (tmp)
|
|
{
|
|
gi.dprintf("-------------------\n");
|
|
gi.dprintf("BANNED IP ADDRESSES\n");
|
|
gi.dprintf("-------------------\n");
|
|
}
|
|
|
|
while (tmp) {
|
|
gi.dprintf("%s\n", tmp->ip);
|
|
|
|
tmp = tmp->next;
|
|
i++;
|
|
}
|
|
|
|
if (!i)
|
|
gi.dprintf("No banned IPs.\n");
|
|
else
|
|
gi.dprintf("%d total banned IPs.\n", i);
|
|
}
|
|
|
|
// kick *and* ban a player all in one shot. Prevents rejoins (obviously)...
|
|
void Adm_KickBan(char *cmd) {
|
|
int no;
|
|
int sv = 0; // changes to 1 if first arg. is "sv" (server command)
|
|
int x = 0;
|
|
|
|
char *tmp_ip;
|
|
char kbstr[32];
|
|
char *term;
|
|
|
|
edict_t *cl_ent;
|
|
|
|
if (!cmd)
|
|
{
|
|
gi.dprintf("usage: sv kickban <player no.>\n");
|
|
return;
|
|
}
|
|
|
|
// check if this is a valid player number
|
|
if ((no = (int) strtol(cmd, &term, 10)) < 0
|
|
|| no >= game.maxclients || *term) {
|
|
gi.dprintf("Invalid player number %s.\n",
|
|
cmd);
|
|
return;
|
|
}
|
|
|
|
cl_ent = g_edicts + 1 + no; // get player
|
|
|
|
if (!cl_ent->inuse) {
|
|
gi.dprintf("Player number %d is not currently in use.\n", no);
|
|
return;
|
|
}
|
|
|
|
// get player's IP
|
|
tmp_ip = Info_ValueForKey(cl_ent->client->pers.userinfo, "ip");
|
|
|
|
// chop port number off of the IP address
|
|
while (++x <= MAX_IP_LENGTH) {
|
|
if (*(tmp_ip + x) == ':' || *(tmp_ip + x) == (char) NULL)
|
|
break;
|
|
}
|
|
|
|
// create ban command line-- need to use the server version
|
|
strcpy(kbstr, "sv ban ");
|
|
strncat(kbstr, tmp_ip, x);
|
|
strcat(kbstr, "\n");
|
|
|
|
// ban!
|
|
gi.AddCommandString(kbstr);
|
|
|
|
// create the kick command line
|
|
strcpy(kbstr, "kick ");
|
|
strncat(kbstr, cmd, 3);
|
|
strcat(kbstr, "\n");
|
|
|
|
//COMMENTED-- is this causing kb to crash the server?
|
|
// // let the player know (sort of)
|
|
// gi.dprintf("Don't come back!\n");
|
|
|
|
// kick!
|
|
gi.AddCommandString(kbstr);
|
|
}
|