bug fixes to ban logic, partial ipv4 addresses work for bans, ban with reasons (ex. ban 192.168.0.0/16 "I hate people on LANs"), addip/removeip/listip can use the same format as ban (and now work for ipv6/ipx), writeip removed and will be fixed/replaced later

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@2295 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
TimeServ 2006-05-25 04:47:03 +00:00
parent c19474d271
commit 116faaf0c9
5 changed files with 275 additions and 191 deletions

View File

@ -580,7 +580,7 @@ qboolean NET_StringToAdr (char *s, netadr_t *a)
// (bits < 0 will always fill all bits)
void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits)
{
int i;
unsigned int i;
qbyte *n;
memset (amask, 0, sizeof(*amask));
@ -608,7 +608,8 @@ void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits)
// fill last bit
if (i)
{
i = (~((1 << i) - 1)) & 0xFF;
i = 8 - i;
i = 255 - ((1 << i) - 1);
*n = i;
}
break;
@ -628,7 +629,8 @@ void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits)
// fill last bit
if (i)
{
i = (~((1 << i) - 1)) & 0xFF;
i = 8 - i;
i = 255 - ((1 << i) - 1);
*n = i;
}
break;
@ -647,13 +649,62 @@ void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits)
// fill last bit
if (i)
{
i = (~((1 << i) - 1)) & 0xFF;
i = 8 - i;
i = 255 - ((1 << i) - 1);
*n = i;
}
break;
}
}
// ParsePartialIPv4: check string to see if it is a partial IPv4 address and
// return bits to mask and set netadr_t or 0 if not an address
int ParsePartialIPv4(char *s, netadr_t *a)
{
char *colon = NULL;
char *address = a->address.ip;
int bits = 8;
if (!*s)
return 0;
memset (a, 0, sizeof(*a));
while (*s)
{
if (*s == ':')
{
if (colon) // only 1 colon
return 0;
colon = s + 1;
}
else if (*s == '.')
{
if (colon) // no colons before periods (probably invalid anyway)
return 0;
else if (bits >= 32) // only 32 bits in ipv4
return 0;
else if (*(s+1) == '.')
return 0;
else if (*(s+1) == '\0')
break; // don't add more bits to the mask for x.x., etc
bits += 8;
address++;
}
else if (*s >= '0' && *s <= '9')
*address = ((*address)*10) + (*s-'0');
else
return 0; // invalid character
s++;
}
a->type = NA_IP;
if (colon)
a->port = atoi(colon);
return bits;
}
// NET_StringToAdrMasked: extension to NET_StringToAdr to handle IP addresses
// with masks or integers representing the bit masks
qboolean NET_StringToAdrMasked (char *s, netadr_t *a, netadr_t *amask)
@ -669,12 +720,12 @@ qboolean NET_StringToAdrMasked (char *s, netadr_t *a, netadr_t *amask)
// we have a slash in the address so split and resolve separately
char *c;
i = spoint - s;
if (i + 1 > sizeof(t))
i = (int)(spoint - s) + 1;
if (i > sizeof(t))
i = sizeof(t);
Q_strncpyz(t, s, i);
if (!NET_StringToAdr(t, a))
if (!ParsePartialIPv4(t, a) && !NET_StringToAdr(t, a))
return false;
spoint++;
@ -693,7 +744,7 @@ qboolean NET_StringToAdrMasked (char *s, netadr_t *a, netadr_t *amask)
}
if (c == NULL) // we have an address so resolve it and return
return NET_StringToAdr(spoint, amask);
return ParsePartialIPv4(spoint, amask) || NET_StringToAdr(spoint, amask);
// otherwise generate mask for given bits
i = atoi(spoint);
@ -702,13 +753,17 @@ qboolean NET_StringToAdrMasked (char *s, netadr_t *a, netadr_t *amask)
else
{
// we don't have a slash, resolve and fill with a full mask
if (!NET_StringToAdr(s, a))
i = ParsePartialIPv4(s, a);
if (!i && !NET_StringToAdr(s, a))
return false;
memset (amask, 0, sizeof(*amask));
amask->type = a->type;
NET_IntegerToMask(a, amask, -1);
if (i)
NET_IntegerToMask(a, amask, i);
else
NET_IntegerToMask(a, amask, -1);
}
return true;
@ -774,7 +829,7 @@ int UniformMaskedBits(netadr_t mask)
{
int bits;
int b;
int bs;
unsigned int bs;
qboolean bitenc = false;
switch (mask.type)
@ -788,7 +843,7 @@ int UniformMaskedBits(netadr_t mask)
bitenc = true;
else if (mask.address.ip[b])
{
bs = ~mask.address.ip[b];
bs = (~mask.address.ip[b]) & 0xFF;
while (bs)
{
if (bs & 1)
@ -818,7 +873,7 @@ int UniformMaskedBits(netadr_t mask)
bitenc = true;
else if (mask.address.ip6[b])
{
bs = ~mask.address.ip6[b];
bs = (~mask.address.ip6[b]) & 0xFF;
while (bs)
{
if (bs & 1)
@ -849,7 +904,7 @@ int UniformMaskedBits(netadr_t mask)
bitenc = true;
else if (mask.address.ipx[b])
{
bs = ~mask.address.ipx[b];
bs = (~mask.address.ipx[b]) & 0xFF;
while (bs)
{
if (bs & 1)
@ -887,7 +942,13 @@ char *NET_AdrToStringMasked (netadr_t a, netadr_t amask)
if (i >= 0)
sprintf(s, "%s/%i", NET_AdrToString(a), i);
else
sprintf(s, "%s/%s", NET_AdrToString(a), NET_AdrToString(amask));
{
// has to be done this way due to NET_AdrToString returning a
// static address
Q_strncatz(s, NET_AdrToString(a), sizeof(s));
Q_strncatz(s, "/", sizeof(s));
Q_strncatz(s, NET_AdrToString(amask), sizeof(s));
}
return s;
}

View File

@ -651,8 +651,15 @@ typedef struct bannedips_s {
struct bannedips_s *next;
netadr_t adr;
netadr_t adrmask;
char reason[1];
} bannedips_t;
typedef struct filteredip_s {
struct filteredip_s *next;
netadr_t adr;
netadr_t adrmask;
} filteredips_t;
typedef enum {
GT_PROGS, //q1, qw, h2 are similar enough that we consider it only one game mode. (We don't support the h2 protocol)
GT_QUAKE2, //q2 servers run from a q2 game dll
@ -711,6 +718,7 @@ typedef struct
challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting
bannedips_t *bannedips;
filteredips_t *filteredips;
char progsnames[MAX_PROGS][32];
progsnum_t progsnum[MAX_PROGS];

View File

@ -638,13 +638,21 @@ void SV_BanName_f (void)
{
client_t *cl;
int clnum=-1;
char *reason = NULL;
int reasonsize = 0;
if (Cmd_Argc() < 2)
{
Con_Printf("%s userid|nick\n", Cmd_Argv(0));
Con_Printf("%s userid|nick [reason]\n", Cmd_Argv(0));
return;
}
if (Cmd_Argc() > 2)
{
reason = Cmd_Argv(2);
reasonsize = strlen(reason);
}
while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum)))
if (cl)
{
@ -656,13 +664,15 @@ void SV_BanName_f (void)
continue;
}
nb = Z_Malloc(sizeof(bannedips_t));
nb = Z_Malloc(sizeof(bannedips_t)+reasonsize);
nb->next = svs.bannedips;
nb->adr = cl->netchan.remote_address;
NET_IntegerToMask(&nb->adr, &nb->adrmask, -1); // fill mask
if (*Cmd_Argv(2)) //explicit blocking of all ports of a client ip
nb->adr.port = 0;
svs.bannedips = nb;
if (reasonsize)
Q_strcpy(nb->reason, reason);
SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTWASBANNED, cl->name);
// print directly, because the dropped client won't get the
@ -682,10 +692,12 @@ void SV_BanIP_f (void)
int i;
client_t *cl;
bannedips_t *nb;
char *reason = NULL;
int reasonsize = 0;
if (Cmd_Argc() < 2)
{
Con_Printf("%s address/mask|adress/maskbits\n", Cmd_Argv(0));
Con_Printf("%s address/mask|adress/maskbits [reason]\n", Cmd_Argv(0));
return;
}
@ -701,6 +713,12 @@ void SV_BanIP_f (void)
return;
}
if (Cmd_Argc() > 2)
{
reason = Cmd_Argv(2);
reasonsize = strlen(reason);
}
// loop through clients and kick the ones that match
for (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++)
{
@ -719,11 +737,58 @@ void SV_BanIP_f (void)
}
// add IP and mask to ban list
nb = Z_Malloc(sizeof(bannedips_t));
nb = Z_Malloc(sizeof(bannedips_t)+reasonsize);
nb->next = svs.bannedips;
nb->adr = banadr;
nb->adrmask = banmask;
svs.bannedips = nb;
if (reasonsize)
Q_strcpy(nb->reason, reason);
}
void SV_FilterIP_f (void)
{
netadr_t banadr;
netadr_t banmask;
int i;
client_t *cl;
filteredips_t *nb;
extern cvar_t filterban;
if (Cmd_Argc() < 2)
{
Con_Printf("%s address/mask|adress/maskbits\n", Cmd_Argv(0));
return;
}
if (!NET_StringToAdrMasked(Cmd_Argv(1), &banadr, &banmask))
{
Con_Printf("invalid address or mask\n");
return;
}
if (NET_IsLoopBackAddress(banadr))
{
Con_Printf("You're not allowed to filter loopback!\n");
return;
}
// loop through clients and kick the ones that match
for (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++)
{
if (cl->state<=cs_zombie)
continue;
if (filterban.value && NET_CompareAdrMasked(cl->netchan.remote_address, banadr, banmask))
SV_DropClient (cl);
}
// add IP and mask to filter list
nb = Z_Malloc(sizeof(filteredips_t));
nb->next = svs.filteredips;
nb->adr = banadr;
nb->adrmask = banmask;
svs.filteredips = nb;
}
void SV_BanList_f (void)
@ -733,7 +798,10 @@ void SV_BanList_f (void)
while (nb)
{
Con_Printf("%s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask));
if (nb->reason[0])
Con_Printf("%s, %s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask), nb->reason);
else
Con_Printf("%s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask));
bancount++;
nb = nb->next;
}
@ -741,6 +809,21 @@ void SV_BanList_f (void)
Con_Printf("%i total entries in ban list\n", bancount);
}
void SV_FilterList_f (void)
{
int filtercount = 0;
filteredips_t *nb = svs.filteredips;
while (nb)
{
Con_Printf("%s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask));
filtercount++;
nb = nb->next;
}
Con_Printf("%i total entries in filter list\n", filtercount);
}
void SV_Unban_f (void)
{
qboolean all = false;
@ -766,7 +849,7 @@ void SV_Unban_f (void)
while (nb)
{
nbnext = nb->next;
if (NET_CompareAdrMasked(nb->adr, unbanadr, unbanmask))
if (all || NET_CompareAdrMasked(nb->adr, unbanadr, unbanmask))
{
if (!all)
Con_Printf("unbanned %s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask));
@ -779,6 +862,44 @@ void SV_Unban_f (void)
}
}
void SV_Unfilter_f (void)
{
qboolean all = false;
filteredips_t *nb = svs.filteredips;
filteredips_t *nbnext;
netadr_t unbanadr = {0};
netadr_t unbanmask = {0};
if (Cmd_Argc() < 2)
{
Con_Printf("%s address/mask|address/maskbits|all\n", Cmd_Argv(0));
return;
}
if (!Q_strcasecmp(Cmd_Argv(1), "all"))
all = true;
else if (!NET_StringToAdrMasked(Cmd_Argv(1), &unbanadr, &unbanmask))
{
Con_Printf("invalid address or mask\n");
return;
}
while (nb)
{
nbnext = nb->next;
if (all || NET_CompareAdrMasked(nb->adr, unbanadr, unbanmask))
{
if (!all)
Con_Printf("unfiltered %s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask));
if (svs.filteredips == nb)
svs.filteredips = nbnext;
Z_Free(nb);
}
nb = nbnext;
}
}
void SV_ForceName_f (void)
{
client_t *cl;
@ -1807,6 +1928,16 @@ void SV_InitOperatorCommands (void)
// Cmd_AddCommand ("ban", SV_BanName_f);
Cmd_AddCommand ("status", SV_Status_f);
Cmd_AddCommand ("addip", SV_FilterIP_f);
Cmd_AddCommand ("removeip", SV_Unfilter_f);
Cmd_AddCommand ("listip", SV_FilterList_f);
// Cmd_AddCommand ("filterip", SV_FilterIP_f);
// Cmd_AddCommand ("unfilter", SV_Unfilter_f);
// Cmd_AddCommand ("filterlist", SV_FilterList_f);
// Cmd_AddCommand ("writeip", SV_WriteIP_f);
Cmd_AddCommand ("sv", SV_SendGameCommand_f);
Cmd_AddCommand ("killserver", SV_KillServer_f);

View File

@ -196,6 +196,7 @@ void SV_FixupName(char *in, char *out);
void SV_AcceptClient (netadr_t adr, int userid, char *userinfo);
void Master_Shutdown (void);
void PR_SetPlayerClass(client_t *cl, int classnum, qboolean fromqc);
bannedips_t *SV_BannedAddress (netadr_t *a);
//============================================================================
@ -1757,18 +1758,16 @@ client_t *SVC_DirectConnect(void)
}
}
{
bannedips_t *banip;
for (banip = svs.bannedips; banip; banip=banip->next)
bannedips_t *banip = SV_BannedAddress(&adr);
if (banip)
{
if (NET_CompareAdrMasked(adr, banip->adr, banip->adrmask))
{
SV_RejectMessage (protocol, "You were banned.\nContact the administrator to complain.\n");
return NULL;
}
if (banip->reason[0])
SV_RejectMessage (protocol, "You were banned.\nReason: %s\n", banip->reason);
else
SV_RejectMessage (protocol, "You were banned.\n");
return NULL;
}
//yay, a legit client who we havn't banned yet.
}
edictnum = (newcl-svs.clients)+1;
@ -2091,6 +2090,15 @@ void SVC_RemoteCommand (void)
int i;
char remaining[1024];
{
bannedips_t *banip = SV_BannedAddress(&net_from);
if (banip)
{
Con_Printf ("Rcon from banned ip %s\n", NET_AdrToString (net_from));
return;
}
}
if (!Rcon_Validate ())
{
#ifdef SVRANKING
@ -2362,136 +2370,8 @@ If 0, then only addresses matching the list will be allowed. This lets you easi
==============================================================================
*/
typedef struct
{
unsigned mask;
unsigned compare;
} ipfilter_t;
#define MAX_IPFILTERS 1024
ipfilter_t ipfilters[MAX_IPFILTERS];
int numipfilters;
cvar_t filterban = SCVAR("filterban", "1");
/*
=================
StringToFilter
=================
*/
qboolean StringToFilter (char *s, ipfilter_t *f)
{
char num[128];
int i, j;
qbyte b[4];
qbyte m[4];
for (i=0 ; i<4 ; i++)
{
b[i] = 0;
m[i] = 0;
}
for (i=0 ; i<4 ; i++)
{
if (*s < '0' || *s > '9')
{
Con_Printf ("Bad filter address: %s\n", s);
return false;
}
j = 0;
while (*s >= '0' && *s <= '9')
{
num[j++] = *s++;
}
num[j] = 0;
b[i] = atoi(num);
if (b[i] != 0)
m[i] = 255;
if (!*s)
break;
s++;
}
f->mask = *(unsigned *)m;
f->compare = *(unsigned *)b;
return true;
}
/*
=================
SV_AddIP_f
=================
*/
void SV_AddIP_f (void)
{
int i;
for (i=0 ; i<numipfilters ; i++)
if (ipfilters[i].compare == 0xffffffff)
break; // free spot
if (i == numipfilters)
{
if (numipfilters == MAX_IPFILTERS)
{
Con_Printf ("IP filter list is full\n");
return;
}
numipfilters++;
}
if (!StringToFilter (Cmd_Argv(1), &ipfilters[i]))
ipfilters[i].compare = 0xffffffff;
}
/*
=================
SV_RemoveIP_f
=================
*/
void SV_RemoveIP_f (void)
{
ipfilter_t f;
int i, j;
if (!StringToFilter (Cmd_Argv(1), &f))
return;
for (i=0 ; i<numipfilters ; i++)
if (ipfilters[i].mask == f.mask
&& ipfilters[i].compare == f.compare)
{
for (j=i+1 ; j<numipfilters ; j++)
ipfilters[j-1] = ipfilters[j];
numipfilters--;
Con_Printf ("Removed.\n");
return;
}
Con_Printf ("Didn't find %s.\n", Cmd_Argv(1));
}
/*
=================
SV_ListIP_f
=================
*/
void SV_ListIP_f (void)
{
int i;
qbyte b[4];
Con_Printf ("Filter list:\n");
for (i=0 ; i<numipfilters ; i++)
{
*(unsigned *)b = ipfilters[i].compare;
Con_Printf ("%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]);
}
}
/*
=================
SV_WriteIP_f
@ -2499,6 +2379,8 @@ SV_WriteIP_f
*/
void SV_WriteIP_f (void)
{
// TODO: function needs to be rewritten to handle new banning and filtering logic
/*
vfsfile_t *f;
char name[MAX_OSPATH];
qbyte b[4];
@ -2524,23 +2406,7 @@ void SV_WriteIP_f (void)
}
VFS_CLOSE (f);
}
/*
=================
SV_SendBan
=================
*/
void SV_SendBan (void)
{
char data[128];
data[0] = data[1] = data[2] = data[3] = 0xff;
data[4] = A2C_PRINT;
data[5] = 0;
strcat (data, "\nbanned.\n");
NET_SendPacket (NS_SERVER, strlen(data), data, net_from);
}
/*
@ -2548,20 +2414,34 @@ void SV_SendBan (void)
SV_FilterPacket
=================
*/
qboolean SV_FilterPacket (void)
qboolean SV_FilterPacket (netadr_t *a)
{
int i;
unsigned in;
filteredips_t *banip;
in = *(unsigned *)net_from.address.ip;
if (NET_IsLoopBackAddress(*a))
return 0; // never filter loopback
for (i=0 ; i<numipfilters ; i++)
if ( (in & ipfilters[i].mask) == ipfilters[i].compare)
for (banip = svs.filteredips; banip; banip=banip->next)
{
if (NET_CompareAdrMasked(*a, banip->adr, banip->adrmask))
return filterban.value;
}
return !filterban.value;
}
// SV_BannedAdress, run through ban address list and return corresponding bannedips_t
// pointer, otherwise return NULL if not in the list
bannedips_t *SV_BannedAddress (netadr_t *a)
{
bannedips_t *banip;
for (banip = svs.bannedips; banip; banip=banip->next)
{
if (NET_CompareAdrMasked(*a, banip->adr, banip->adrmask))
return banip;
}
return NULL;
}
//send a network packet to a new non-connected client.
//this is to combat tunneling
void SV_OpenRoute_f(void)
@ -2598,11 +2478,8 @@ void SV_ReadPackets (void)
good = false;
while (SV_GetPacket ())
{
if (SV_FilterPacket ())
{
SV_SendBan (); // tell them we aren't listening...
if (SV_FilterPacket (&net_from))
continue;
}
// check for connectionless packet (0xffffffff) first
if (*(int *)net_message.data == -1)
@ -3235,11 +3112,6 @@ void SV_InitLocal (void)
Cvar_Register (&sv_nailhack, cvargroup_servercontrol);
Cmd_AddCommand ("addip", SV_AddIP_f);
Cmd_AddCommand ("removeip", SV_RemoveIP_f);
Cmd_AddCommand ("listip", SV_ListIP_f);
Cmd_AddCommand ("writeip", SV_WriteIP_f);
Cmd_AddCommand ("sv_impulse", SV_Impulse_f);
Cmd_AddCommand ("openroute", SV_OpenRoute_f);

View File

@ -2911,6 +2911,7 @@ void SVQ3_HandleClient(void)
SVQ3_ParseClientMessage(&svs.clients[i]);
}
bannedips_t *SV_BannedAddress (netadr_t *a);
void SVQ3_DirectConnect(void) //Actually connect the client, use up a slot, and let the gamecode know of it.
{
char *reason;
@ -2919,6 +2920,7 @@ void SVQ3_DirectConnect(void) //Actually connect the client, use up a slot, and
int ret;
int challenge;
int qport;
bannedips_t *banip;
if (net_message.cursize < 13)
return;
@ -2934,7 +2936,17 @@ void SVQ3_DirectConnect(void) //Actually connect the client, use up a slot, and
if (!cl)
cl = SVQ3_FindEmptyPlayerSlot();
if (!cl)
banip = SV_BannedAddress(&net_from);
if (banip)
{
if (banip->reason[0])
reason = banip->reason;
else
reason = "Banned.";
userinfo = NULL;
}
else if (!cl)
{
reason = "Server is full.";
userinfo = NULL;