|
|
@ -17,21 +17,13 @@ GNU General Public License for more details. |
|
|
|
#include "server.h" |
|
|
|
#include "server.h" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct ipfilter_s |
|
|
|
/*
|
|
|
|
{ |
|
|
|
============================================================================= |
|
|
|
float time; |
|
|
|
|
|
|
|
float endTime; // -1 for permanent ban
|
|
|
|
|
|
|
|
struct ipfilter_s *next; |
|
|
|
|
|
|
|
uint mask; |
|
|
|
|
|
|
|
uint ip; |
|
|
|
|
|
|
|
} ipfilter_t; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static ipfilter_t *ipfilter = NULL; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Is IP filter really needed?
|
|
|
|
PLAYER ID FILTER |
|
|
|
// TODO: Make it IPv6 compatible, for future expansion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
============================================================================= |
|
|
|
|
|
|
|
*/ |
|
|
|
typedef struct cidfilter_s |
|
|
|
typedef struct cidfilter_s |
|
|
|
{ |
|
|
|
{ |
|
|
|
float endTime; |
|
|
|
float endTime; |
|
|
@ -67,32 +59,6 @@ static void SV_RemoveID( const char *id ) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void SV_RemoveIP( uint ip, uint mask ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
ipfilter_t *filter, *prevfilter = NULL; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for( filter = ipfilter; filter; filter = filter->next ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if( filter->ip != ip || mask != filter->mask ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
prevfilter = filter; |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( filter == ipfilter ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
ipfilter = ipfilter->next; |
|
|
|
|
|
|
|
Mem_Free( filter ); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( prevfilter ) |
|
|
|
|
|
|
|
prevfilter->next = filter->next; |
|
|
|
|
|
|
|
Mem_Free( filter ); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
qboolean SV_CheckID( const char *id ) |
|
|
|
qboolean SV_CheckID( const char *id ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
qboolean ret = false; |
|
|
|
qboolean ret = false; |
|
|
@ -122,34 +88,6 @@ qboolean SV_CheckID( const char *id ) |
|
|
|
return ret; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
qboolean SV_CheckIP( netadr_t *addr ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
uint ip = addr->ip[0] << 24 | addr->ip[1] << 16 | addr->ip[2] << 8 | addr->ip[3]; |
|
|
|
|
|
|
|
qboolean ret = false; |
|
|
|
|
|
|
|
ipfilter_t *filter; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for( filter = ipfilter; filter; filter = filter->next ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
while( filter->endTime && host.realtime > filter->endTime ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
uint rip = filter->ip; |
|
|
|
|
|
|
|
uint rmask = filter->mask; |
|
|
|
|
|
|
|
SV_RemoveIP( rip, rmask ); |
|
|
|
|
|
|
|
filter = filter->next; |
|
|
|
|
|
|
|
if( !filter ) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( (ip & filter->mask) == (filter->ip & filter->mask) ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
ret = true; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ret; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void SV_BanID_f( void ) |
|
|
|
static void SV_BanID_f( void ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
float time = Q_atof( Cmd_Argv( 1 ) ); |
|
|
|
float time = Q_atof( Cmd_Argv( 1 ) ); |
|
|
@ -289,161 +227,341 @@ static void SV_WriteID_f( void ) |
|
|
|
FS_Close( f ); |
|
|
|
FS_Close( f ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static qboolean StringToIP( const char *str, const char *maskstr, uint *outip, uint *outmask ) |
|
|
|
static void SV_InitIDFilter( void ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
byte ip[4] = {0}; |
|
|
|
Cmd_AddRestrictedCommand( "banid", SV_BanID_f, "ban player by ID" ); |
|
|
|
byte mask[4] = {0}; |
|
|
|
Cmd_AddRestrictedCommand( "listid", SV_ListID_f, "list banned players" ); |
|
|
|
int i = 0; |
|
|
|
Cmd_AddRestrictedCommand( "removeid", SV_RemoveID_f, "remove player from banned list" ); |
|
|
|
|
|
|
|
Cmd_AddRestrictedCommand( "writeid", SV_WriteID_f, "write banned.cfg" ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if( *str > '9' || *str < '0' ) |
|
|
|
static void SV_ShutdownIDFilter( void ) |
|
|
|
return false; |
|
|
|
{ |
|
|
|
|
|
|
|
cidfilter_t *cidList, *cidNext; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// should be called manually because banned.cfg is not executed by engine
|
|
|
|
|
|
|
|
//SV_WriteID_f();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Cmd_RemoveCommand( "banid" ); |
|
|
|
|
|
|
|
Cmd_RemoveCommand( "listid" ); |
|
|
|
|
|
|
|
Cmd_RemoveCommand( "removeid" ); |
|
|
|
|
|
|
|
Cmd_RemoveCommand( "writeid" ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for( cidList = cidfilter; cidList; cidList = cidNext ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
cidNext = cidList->next; |
|
|
|
|
|
|
|
Mem_Free( cidList ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cidfilter = NULL; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
============================================================================= |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CLIENT IP FILTER |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
============================================================================= |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct ipfilter_s |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
float endTime; |
|
|
|
|
|
|
|
struct ipfilter_s *next; |
|
|
|
|
|
|
|
netadr_t adr; |
|
|
|
|
|
|
|
uint prefixlen; |
|
|
|
|
|
|
|
} ipfilter_t; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static ipfilter_t *ipfilter = NULL; |
|
|
|
|
|
|
|
|
|
|
|
do |
|
|
|
static void SV_CleanExpiredIPFilters( void ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
ipfilter_t *f, **back; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
back = &ipfilter; |
|
|
|
|
|
|
|
while( 1 ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
while( *str <= '9' && *str >= '0' ) |
|
|
|
f = *back; |
|
|
|
|
|
|
|
if( !f ) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( f->endTime && host.realtime > f->endTime ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
ip[i] *=10; |
|
|
|
*back = f->next; |
|
|
|
ip[i] += *str - '0'; |
|
|
|
back = &f->next; |
|
|
|
str++; |
|
|
|
|
|
|
|
|
|
|
|
Mem_Free( f ); |
|
|
|
} |
|
|
|
} |
|
|
|
mask[i] = 255; |
|
|
|
else back = &f->next; |
|
|
|
i++; |
|
|
|
} |
|
|
|
if( *str != '.' ) break; |
|
|
|
} |
|
|
|
str++; |
|
|
|
|
|
|
|
} while( i < 4 ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i = 0; |
|
|
|
static int SV_FilterToString( char *dest, size_t size, qboolean config, ipfilter_t *f ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
const char *strformat; |
|
|
|
|
|
|
|
|
|
|
|
if( !maskstr || *maskstr > '9' || *maskstr < '0' ) |
|
|
|
if( config ) |
|
|
|
goto end; |
|
|
|
{ |
|
|
|
|
|
|
|
return Q_snprintf( dest, size, "addip 0 %s/%d\n", NET_AdrToString( f->adr ), f->prefixlen ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else if( f->endTime ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return Q_snprintf( dest, size, "%s/%d (%f minutes)", NET_AdrToString( f->adr ), f->prefixlen, f->endTime ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
do |
|
|
|
return Q_snprintf( dest, size, "%s/%d (permanent)", NET_AdrToString( f->adr ), f->prefixlen ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static qboolean SV_IPFilterIncludesIPFilter( ipfilter_t *a, ipfilter_t *b ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if( a->adr.type6 != b->adr.type6 ) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// can't include bigger subnet in small
|
|
|
|
|
|
|
|
if( a->prefixlen < b->prefixlen ) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( a->prefixlen == b->prefixlen ) |
|
|
|
|
|
|
|
return NET_CompareAdr( a->adr, b->adr ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return NET_CompareAdrByMask( a->adr, b->adr, b->prefixlen ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void SV_RemoveIPFilter( ipfilter_t *toremove, qboolean removeAll, qboolean verbose ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
ipfilter_t *f, **back; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
back = &ipfilter; |
|
|
|
|
|
|
|
while( 1 ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
f = *back; |
|
|
|
|
|
|
|
if( !f ) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( SV_IPFilterIncludesIPFilter( toremove, f )) |
|
|
|
{ |
|
|
|
{ |
|
|
|
byte mask1 = 0; |
|
|
|
if( verbose ) |
|
|
|
while( *maskstr <= '9' && *maskstr >= '0' ) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
mask1 *=10; |
|
|
|
string filterStr; |
|
|
|
mask1 += *maskstr - '0'; |
|
|
|
|
|
|
|
maskstr++; |
|
|
|
SV_FilterToString( filterStr, sizeof( filterStr ), false, f ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Con_Printf( "%s removed.\n", filterStr ); |
|
|
|
} |
|
|
|
} |
|
|
|
mask[i] &= mask1; |
|
|
|
|
|
|
|
i++; |
|
|
|
|
|
|
|
if( *maskstr != '.' ) break; |
|
|
|
|
|
|
|
maskstr++; |
|
|
|
|
|
|
|
} while( i < 4 ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
end: |
|
|
|
*back = f->next; |
|
|
|
*outip = ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3]; |
|
|
|
back = &f->next; |
|
|
|
if( outmask ) |
|
|
|
|
|
|
|
*outmask = mask[0] << 24 | mask[1] << 16 | mask[2] << 8 | mask[3]; |
|
|
|
Mem_Free( f ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( !removeAll ) |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else back = &f->next; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
qboolean SV_CheckIP( netadr_t *adr ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// TODO: ip rate limit
|
|
|
|
|
|
|
|
ipfilter_t *entry = ipfilter; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for( ; entry; entry = entry->next ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
switch( entry->adr.type6 ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
case NA_IP: |
|
|
|
|
|
|
|
case NA_IP6: |
|
|
|
|
|
|
|
if( NET_CompareAdrByMask( *adr, entry->adr, entry->prefixlen )) |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void SV_AddIP_PrintUsage( void ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Con_Printf(S_USAGE "addip <minutes> <ipaddress>\n" |
|
|
|
|
|
|
|
S_USAGE_INDENT "addip <minutes> <ipaddress/CIDR>\n" |
|
|
|
|
|
|
|
"Use 0 minutes for permanent\n" |
|
|
|
|
|
|
|
"ipaddress A.B.C.D/24 is equivalent to A.B.C.0 and A.B.C\n" |
|
|
|
|
|
|
|
"NOTE: IPv6 addresses only support prefix format!\n"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void SV_RemoveIP_PrintUsage( void ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Con_Printf(S_USAGE "removeip <ipaddress> [removeAll]\n" |
|
|
|
|
|
|
|
S_USAGE_INDENT "removeip <ipaddress/CIDR> [removeAll]\n" |
|
|
|
|
|
|
|
"Use removeAll to delete all ip filters which ipaddress or ipaddress/CIDR includes\n"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void SV_ListIP_PrintUsage( void ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Con_Printf(S_USAGE "listip [ipaddress]\n" |
|
|
|
|
|
|
|
S_USAGE_INDENT "listip [ipaddress/CIDR]\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#define IPARGS(ip) (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF |
|
|
|
|
|
|
|
static void SV_AddIP_f( void ) |
|
|
|
static void SV_AddIP_f( void ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
float time = Q_atof( Cmd_Argv( 1 ) ); |
|
|
|
const char *szMinutes = Cmd_Argv( 1 ); |
|
|
|
const char *ipstr = Cmd_Argv( 2 ); |
|
|
|
const char *adr = Cmd_Argv( 2 ); |
|
|
|
const char *maskstr = Cmd_Argv( 3 ); |
|
|
|
ipfilter_t filter, *newfilter; |
|
|
|
uint ip, mask; |
|
|
|
float minutes; |
|
|
|
ipfilter_t *filter; |
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
|
|
if( time ) |
|
|
|
if( Cmd_Argc() != 3 ) |
|
|
|
time = host.realtime + time * 60.0f; |
|
|
|
{ |
|
|
|
|
|
|
|
// a1ba: kudos to rehlds for an idea of using CIDR prefixes
|
|
|
|
|
|
|
|
// in these commands :)
|
|
|
|
|
|
|
|
SV_AddIP_PrintUsage(); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if( !StringToIP( ipstr, maskstr, &ip, &mask ) ) |
|
|
|
minutes = Q_atof( szMinutes ); |
|
|
|
|
|
|
|
if( minutes < 0.1f ) |
|
|
|
|
|
|
|
minutes = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( minutes != 0.0f ) |
|
|
|
|
|
|
|
filter.endTime = host.realtime + minutes * 60; |
|
|
|
|
|
|
|
else filter.endTime = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( !NET_StringToFilterAdr( adr, &filter.adr, &filter.prefixlen ) ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Con_Reportf( "Usage: addip <minutes> <ip> [mask]\n0 minutes for permanent ban\n"); |
|
|
|
Con_Printf( "Invalid IP address!\n" ); |
|
|
|
|
|
|
|
SV_AddIP_PrintUsage(); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
SV_RemoveIP( ip, mask ); |
|
|
|
newfilter = Mem_Malloc( host.mempool, sizeof( *newfilter )); |
|
|
|
|
|
|
|
newfilter->endTime = filter.endTime; |
|
|
|
|
|
|
|
newfilter->adr = filter.adr; |
|
|
|
|
|
|
|
newfilter->prefixlen = filter.prefixlen; |
|
|
|
|
|
|
|
newfilter->next = ipfilter; |
|
|
|
|
|
|
|
|
|
|
|
filter = Mem_Malloc( host.mempool, sizeof( ipfilter_t ) ); |
|
|
|
ipfilter = newfilter; |
|
|
|
filter->endTime = time; |
|
|
|
|
|
|
|
filter->ip = ip; |
|
|
|
for( i = 0; i < svs.maxclients; i++ ) |
|
|
|
filter->mask = mask; |
|
|
|
{ |
|
|
|
filter->next = ipfilter; |
|
|
|
netadr_t clientadr = svs.clients[i].netchan.remote_address; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( !NET_CompareAdrByMask( clientadr, filter.adr, filter.prefixlen )) |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
ipfilter = filter; |
|
|
|
SV_ClientPrintf( &svs.clients[i], "The server operator has added you to banned list\n" ); |
|
|
|
|
|
|
|
SV_DropClient( &svs.clients[i], false ); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void SV_ListIP_f( void ) |
|
|
|
static void SV_ListIP_f( void ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
ipfilter_t *filter; |
|
|
|
qboolean haveFilter = false; |
|
|
|
|
|
|
|
ipfilter_t filter, *f; |
|
|
|
|
|
|
|
|
|
|
|
Con_Reportf( "ip ban list\n" ); |
|
|
|
if( Cmd_Argc() > 2 ) |
|
|
|
Con_Reportf( "-----------\n" ); |
|
|
|
{ |
|
|
|
|
|
|
|
SV_ListIP_PrintUsage(); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for( filter = ipfilter; filter; filter = filter->next ) |
|
|
|
if( ipfilter == NULL ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if( filter->endTime && host.realtime > filter->endTime ) |
|
|
|
Con_Printf( "IP filter list is empty\n" ); |
|
|
|
continue; // no negative time
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if( filter->endTime ) |
|
|
|
if( Cmd_Argc() == 2 ) |
|
|
|
Con_Reportf( "%d.%d.%d.%d %d.%d.%d.%d expries in %f minutes\n", IPARGS( filter->ip ), IPARGS( filter->mask ), ( filter->endTime - host.realtime ) / 60.0f ); |
|
|
|
{ |
|
|
|
else |
|
|
|
haveFilter = NET_StringToFilterAdr( Cmd_Argv( 1 ), &filter.adr, &filter.prefixlen ); |
|
|
|
Con_Reportf( "%d.%d.%d.%d %d.%d.%d.%d permanent\n", IPARGS( filter->ip ), IPARGS( filter->mask ) ); |
|
|
|
|
|
|
|
|
|
|
|
if( !haveFilter ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Con_Printf( "Invalid IP address!\n" ); |
|
|
|
|
|
|
|
SV_ListIP_PrintUsage(); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Con_Printf( "IP filter list:\n" ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for( f = ipfilter; f; f = f->next ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
string filterStr; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( haveFilter && !SV_IPFilterIncludesIPFilter( &filter, f )) |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SV_FilterToString( filterStr, sizeof( filterStr ), false, f ); |
|
|
|
|
|
|
|
Con_Printf( "%s\n", filterStr ); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void SV_RemoveIP_f( void ) |
|
|
|
static void SV_RemoveIP_f( void ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
uint ip, mask; |
|
|
|
const char *adr = Cmd_Argv( 1 ); |
|
|
|
|
|
|
|
qboolean removeAll; |
|
|
|
|
|
|
|
ipfilter_t filter; |
|
|
|
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
|
|
if( !StringToIP( Cmd_Argv(1), Cmd_Argv(2), &ip, &mask ) ) |
|
|
|
if( Cmd_Argc() != 2 && Cmd_Argc() != 3 ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Con_Reportf( "Usage: removeip <ip> [mask]\n" ); |
|
|
|
SV_RemoveIP_PrintUsage(); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
SV_RemoveIP( ip, mask ); |
|
|
|
removeAll = Cmd_Argc() == 3 && !Q_strcmp( Cmd_Argv( 2 ), "removeAll" ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( !NET_StringToFilterAdr( adr, &filter.adr, &filter.prefixlen ) ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Con_Printf( "Invalid IP address!\n" ); |
|
|
|
|
|
|
|
SV_RemoveIP_PrintUsage(); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SV_RemoveIPFilter( &filter, removeAll, true ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void SV_WriteIP_f( void ) |
|
|
|
static void SV_WriteIP_f( void ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
file_t *f = FS_Open( Cvar_VariableString( "listipcfgfile" ), "w", false ); |
|
|
|
file_t *fd = FS_Open( Cvar_VariableString( "listipcfgfile" ), "w", true ); |
|
|
|
ipfilter_t *filter; |
|
|
|
ipfilter_t *f; |
|
|
|
|
|
|
|
|
|
|
|
if( !f ) |
|
|
|
if( !fd ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Con_DPrintf( S_ERROR "Could not write %s\n", Cvar_VariableString( "listipcfgfile" ) ); |
|
|
|
Con_Printf( "Couldn't open listip.cfg\n" ); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
FS_Printf( f, "//=======================================================================\n" ); |
|
|
|
for( f = ipfilter; f; f = f->next ) |
|
|
|
FS_Printf( f, "//\t\tCopyright Flying With Gauss Team %s ©\n", Q_timestamp( TIME_YEAR_ONLY )); |
|
|
|
{ |
|
|
|
FS_Printf( f, "//\t\t %s - archive of IP blacklist\n", Cvar_VariableString( "listipcfgfile" ) ); |
|
|
|
string filterStr; |
|
|
|
FS_Printf( f, "//=======================================================================\n" ); |
|
|
|
int size; |
|
|
|
|
|
|
|
|
|
|
|
for( filter = ipfilter; filter; filter = filter->next ) |
|
|
|
// do not save temporary bans
|
|
|
|
if( !filter->endTime ) // only permanent
|
|
|
|
if( f->endTime ) |
|
|
|
FS_Printf( f, "addip 0 %d.%d.%d.%d %d.%d.%d.%d\n", IPARGS(filter->ip), IPARGS(filter->mask) ); |
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
FS_Close( f ); |
|
|
|
size = SV_FilterToString( filterStr, sizeof( filterStr ), true, f ); |
|
|
|
|
|
|
|
FS_Write( fd, filterStr, size ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FS_Close( fd ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void SV_InitFilter( void ) |
|
|
|
static void SV_InitIPFilter( void ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Cmd_AddRestrictedCommand( "banid", SV_BanID_f, "ban player by ID" ); |
|
|
|
|
|
|
|
Cmd_AddRestrictedCommand( "listid", SV_ListID_f, "list banned players" ); |
|
|
|
|
|
|
|
Cmd_AddRestrictedCommand( "removeid", SV_RemoveID_f, "remove player from banned list" ); |
|
|
|
|
|
|
|
Cmd_AddRestrictedCommand( "writeid", SV_WriteID_f, "write banned.cfg" ); |
|
|
|
|
|
|
|
Cmd_AddRestrictedCommand( "addip", SV_AddIP_f, "add entry to IP filter" ); |
|
|
|
Cmd_AddRestrictedCommand( "addip", SV_AddIP_f, "add entry to IP filter" ); |
|
|
|
Cmd_AddRestrictedCommand( "listip", SV_ListIP_f, "list current IP filter" ); |
|
|
|
Cmd_AddRestrictedCommand( "listip", SV_ListIP_f, "list current IP filter" ); |
|
|
|
Cmd_AddRestrictedCommand( "removeip", SV_RemoveIP_f, "remove IP filter" ); |
|
|
|
Cmd_AddRestrictedCommand( "removeip", SV_RemoveIP_f, "remove IP filter" ); |
|
|
|
Cmd_AddRestrictedCommand( "writeip", SV_WriteIP_f, "write listip.cfg" ); |
|
|
|
Cmd_AddRestrictedCommand( "writeip", SV_WriteIP_f, "write listip.cfg" ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void SV_ShutdownFilter( void ) |
|
|
|
static void SV_ShutdownIPFilter( void ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
ipfilter_t *ipList, *ipNext; |
|
|
|
ipfilter_t *ipList, *ipNext; |
|
|
|
cidfilter_t *cidList, *cidNext; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// should be called manually because banned.cfg is not executed by engine
|
|
|
|
// should be called manually because banned.cfg is not executed by engine
|
|
|
|
//SV_WriteIP_f();
|
|
|
|
//SV_WriteIP_f();
|
|
|
|
//SV_WriteID_f();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for( ipList = ipfilter; ipList; ipList = ipNext ) |
|
|
|
for( ipList = ipfilter; ipList; ipList = ipNext ) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -451,12 +569,148 @@ void SV_ShutdownFilter( void ) |
|
|
|
Mem_Free( ipList ); |
|
|
|
Mem_Free( ipList ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for( cidList = cidfilter; cidList; cidList = cidNext ) |
|
|
|
ipfilter = NULL; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SV_InitFilter( void ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
SV_InitIPFilter(); |
|
|
|
|
|
|
|
SV_InitIDFilter(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SV_ShutdownFilter( void ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
SV_ShutdownIPFilter(); |
|
|
|
|
|
|
|
SV_ShutdownIDFilter(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if XASH_ENGINE_TESTS |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "tests.h" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Test_StringToFilterAdr( void ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
ipfilter_t f1; |
|
|
|
|
|
|
|
int i; |
|
|
|
|
|
|
|
struct |
|
|
|
{ |
|
|
|
{ |
|
|
|
cidNext = cidList->next; |
|
|
|
const char *str; |
|
|
|
Mem_Free( cidList ); |
|
|
|
qboolean valid; |
|
|
|
|
|
|
|
int prefixlen; |
|
|
|
|
|
|
|
int a, b, c, d; |
|
|
|
|
|
|
|
} ipv4tests[] = |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
{ "127.0.0.0/8", true, 8, 127, 0, 0, 0 }, |
|
|
|
|
|
|
|
{ "192.168", true, 16, 192, 168, 0, 0 }, |
|
|
|
|
|
|
|
{ "192.168/23", true, 23, 192, 168, 0, 0 }, |
|
|
|
|
|
|
|
{ "192.168./23", true, 23, 192, 168, 0, 0 }, |
|
|
|
|
|
|
|
{ "192.168../23", true, 23, 192, 168, 0, 0 }, |
|
|
|
|
|
|
|
{ "..192...168/23", false }, |
|
|
|
|
|
|
|
{ "", false }, |
|
|
|
|
|
|
|
{ "abcd", false } |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
struct |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
const char *str; |
|
|
|
|
|
|
|
qboolean valid; |
|
|
|
|
|
|
|
int prefixlen; |
|
|
|
|
|
|
|
uint8_t x[16]; |
|
|
|
|
|
|
|
} ipv6tests[] = |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
{ "::1", true, 128, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, |
|
|
|
|
|
|
|
{ "fd18:b9d4:65cf:83de::/64", true, 64, { 0xfd, 0x18, 0xb9, 0xd4, 0x65, 0xcf, 0x83, 0xde } }, |
|
|
|
|
|
|
|
{ "kkljnljkhfjnkj", false }, |
|
|
|
|
|
|
|
{ "fd8a:63d5:e014:0d62:ffff:ffff:ffff:ffff:ffff", false }, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < ARRAYSIZE( ipv4tests ); i++ ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
qboolean ret = NET_StringToFilterAdr( ipv4tests[i].str, &f1.adr, &f1.prefixlen ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TASSERT_EQi( ret, ipv4tests[i].valid ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( ret ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
TASSERT_EQi( f1.prefixlen, ipv4tests[i].prefixlen ); |
|
|
|
|
|
|
|
TASSERT_EQi( f1.adr.ip[0], ipv4tests[i].a ); |
|
|
|
|
|
|
|
TASSERT_EQi( f1.adr.ip[1], ipv4tests[i].b ); |
|
|
|
|
|
|
|
TASSERT_EQi( f1.adr.ip[2], ipv4tests[i].c ); |
|
|
|
|
|
|
|
TASSERT_EQi( f1.adr.ip[3], ipv4tests[i].d ); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
cidfilter = NULL; |
|
|
|
for( i = 0; i < ARRAYSIZE( ipv6tests ); i++ ) |
|
|
|
ipfilter = NULL; |
|
|
|
{ |
|
|
|
|
|
|
|
qboolean ret = NET_StringToFilterAdr( ipv6tests[i].str, &f1.adr, &f1.prefixlen ); |
|
|
|
|
|
|
|
uint8_t x[16]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TASSERT_EQi( ret, ipv6tests[i].valid ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( ret ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
TASSERT_EQi( f1.prefixlen, ipv6tests[i].prefixlen ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NET_NetadrToIP6Bytes( (uint8_t*)x, &f1.adr ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TASSERT( memcmp( x, ipv6tests[i].x, sizeof( x )) == 0 ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Test_IPFilterIncludesIPFilter( void ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
qboolean ret; |
|
|
|
|
|
|
|
const char *adrs[] = |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
"127.0.0.1/8", // 0
|
|
|
|
|
|
|
|
"127.0.0.1", // 1
|
|
|
|
|
|
|
|
"192.168/16", // 2
|
|
|
|
|
|
|
|
"fe80::/64", // 3
|
|
|
|
|
|
|
|
"fe80::96ab:9a49:2944:1808", // 4
|
|
|
|
|
|
|
|
"2a00:1370:8190:f9eb::/62", // 5
|
|
|
|
|
|
|
|
"2a00:1370:8190:f9eb:3866:6126:330c:b82b" // 6
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
ipfilter_t f[7]; |
|
|
|
|
|
|
|
int i; |
|
|
|
|
|
|
|
int tests[][3] = |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// ipv4
|
|
|
|
|
|
|
|
{ 0, 0, true }, |
|
|
|
|
|
|
|
{ 0, 1, false }, |
|
|
|
|
|
|
|
{ 1, 0, true }, |
|
|
|
|
|
|
|
{ 0, 2, false }, |
|
|
|
|
|
|
|
{ 2, 0, false }, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// mixed
|
|
|
|
|
|
|
|
{ 0, 3, false }, |
|
|
|
|
|
|
|
{ 1, 4, false }, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ipv6
|
|
|
|
|
|
|
|
{ 3, 3, true }, |
|
|
|
|
|
|
|
{ 3, 4, false }, |
|
|
|
|
|
|
|
{ 4, 3, true }, |
|
|
|
|
|
|
|
{ 5, 3, false }, |
|
|
|
|
|
|
|
{ 3, 5, false }, |
|
|
|
|
|
|
|
{ 6, 5, true }, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < 7; i++ ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
NET_StringToFilterAdr( adrs[i], &f[i].adr, &f[i].prefixlen ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < ARRAYSIZE( tests ); i++ ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
ret = SV_IPFilterIncludesIPFilter( &f[tests[i][0]], &f[tests[i][1]] ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TASSERT_EQi( ret, tests[i][2] ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Test_RunIPFilter( void ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Test_StringToFilterAdr(); |
|
|
|
|
|
|
|
Test_IPFilterIncludesIPFilter(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif // XASH_ENGINE_TESTS
|
|
|
|