aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README23
-rw-r--r--code/client/cl_main.c2
-rw-r--r--code/qcommon/files.c3
-rw-r--r--code/qcommon/qcommon.h1
-rw-r--r--code/server/server.h15
-rw-r--r--code/server/sv_ccmds.c335
-rw-r--r--code/server/sv_client.c95
-rw-r--r--code/server/sv_init.c3
-rw-r--r--code/server/sv_main.c3
9 files changed, 477 insertions, 3 deletions
diff --git a/README b/README
index dc4131f..affde4c 100644
--- a/README
+++ b/README
@@ -143,11 +143,34 @@ New cvars
sv_dlURL - the base of the HTTP or FTP site that
holds custom pk3 files for your server
+ net_ip6 - IPv6 address to bind to
+ net_port6 - port to bind to using the ipv6 address
+ net_enabled - enable networking, bitmask. Add up
+ number for option to enable it:
+ enable ipv4 networking: 1
+ enable ipv6 networking: 2
+ prioritise ipv6 over ipv4: 4
+ disable multicast support: 8
+ net_mcast6addr - multicast address to use for scanning for
+ ipv6 servers on the local network
+ net_mcastiface - outgoing interface to use for scan
+
+
New commands
video [filename] - start video capture (use with demo command)
stopvideo - stop video capture
+
print - print out the contents of a cvar
+ banaddr <range> - ban an ip address range from joining a game on this
+ server, valid <range> is either playernum or CIDR
+ notation address range.
+ exceptaddr <range> - exempt an ip address range from a ban.
+ bandel <num> - delete ban <num>
+ exceptdel <num> - delete exception <num>
+ listbans - list all currently active bans and exceptions
+ rehashbans - reload the banlist from serverbans.dat
+ flushbans - delete all bans
------------------------------------------------------------ Miscellaneous -----
diff --git a/code/client/cl_main.c b/code/client/cl_main.c
index c2edd02..3c8625e 100644
--- a/code/client/cl_main.c
+++ b/code/client/cl_main.c
@@ -2784,7 +2784,7 @@ void CL_Init( void ) {
SCR_Init ();
- Cbuf_Execute ();
+// Cbuf_Execute ();
Cvar_Set( "cl_running", "1" );
diff --git a/code/qcommon/files.c b/code/qcommon/files.c
index a5efb7e..07ecfac 100644
--- a/code/qcommon/files.c
+++ b/code/qcommon/files.c
@@ -715,7 +715,8 @@ int FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp ) {
if (f) {
return FS_filelength(f);
}
- return 0;
+
+ return -1;
}
diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h
index 8746f3b..396042e 100644
--- a/code/qcommon/qcommon.h
+++ b/code/qcommon/qcommon.h
@@ -583,6 +583,7 @@ int FS_GetFileList( const char *path, const char *extension, char *listbuf, in
int FS_GetModList( char *listbuf, int bufsize );
fileHandle_t FS_FOpenFileWrite( const char *qpath );
+fileHandle_t FS_FOpenFileAppend( const char *filename );
// will properly create any needed paths and deal with seperater character issues
int FS_filelength( fileHandle_t f );
diff --git a/code/server/server.h b/code/server/server.h
index 6a7612b..3d03aee 100644
--- a/code/server/server.h
+++ b/code/server/server.h
@@ -213,6 +213,18 @@ typedef struct {
netadr_t authorizeAddress; // for rcon return messages
} serverStatic_t;
+#define SERVER_MAXBANS 1024
+#define SERVER_BANFILE "serverbans.dat"
+// Structure for managing bans
+typedef struct
+{
+ netadr_t ip;
+ // For a CIDR-Notation type suffix
+ int subnet;
+
+ qboolean isexception;
+} serverBan_t;
+
//=============================================================================
extern serverStatic_t svs; // persistant server info across maps
@@ -249,6 +261,9 @@ extern cvar_t *sv_floodProtect;
extern cvar_t *sv_lanForceRate;
extern cvar_t *sv_strictAuth;
+extern serverBan_t serverBans[SERVER_MAXBANS];
+extern int serverBansCount;
+
//===========================================================
//
diff --git a/code/server/sv_ccmds.c b/code/server/sv_ccmds.c
index e2bcd93..ad49334 100644
--- a/code/server/sv_ccmds.c
+++ b/code/server/sv_ccmds.c
@@ -513,6 +513,333 @@ static void SV_BanNum_f( void ) {
/*
==================
+SV_RehashBans_f
+
+Load saved bans from file.
+==================
+*/
+void SV_RehashBans_f(void)
+{
+ int index, filelen;
+ fileHandle_t readfrom;
+ char *textbuf, *curpos, *maskpos, *newlinepos, *endpos;
+ char filepath[MAX_QPATH];
+
+ serverBansCount = 0;
+
+ if(!(curpos = Cvar_VariableString("fs_game")) || !*curpos)
+ curpos = BASEGAME;
+
+ Com_sprintf(filepath, sizeof(filepath), "%s/%s", curpos, SERVER_BANFILE);
+
+ if((filelen = FS_SV_FOpenFileRead(filepath, &readfrom)) >= 0)
+ {
+ if(filelen < 2)
+ {
+ // Don't bother if file is too short.
+ FS_FCloseFile(readfrom);
+ return;
+ }
+
+ curpos = textbuf = Z_Malloc(filelen);
+
+ filelen = FS_Read(textbuf, filelen, readfrom);
+ FS_FCloseFile(readfrom);
+
+ endpos = textbuf + filelen;
+
+ for(index = 0; index < SERVER_MAXBANS && curpos + 2 < endpos; index++)
+ {
+ // find the end of the address string
+ for(maskpos = curpos + 2; maskpos < endpos && *maskpos != ' '; maskpos++);
+
+ if(maskpos + 1 >= endpos)
+ break;
+
+ *maskpos = '\0';
+ maskpos++;
+
+ // find the end of the subnet specifier
+ for(newlinepos = maskpos; newlinepos < endpos && *newlinepos != '\n'; newlinepos++);
+
+ if(newlinepos >= endpos)
+ break;
+
+ *newlinepos = '\0';
+
+ if(NET_StringToAdr(curpos + 2, &serverBans[index].ip, NA_UNSPEC))
+ {
+ serverBans[index].isexception = !(curpos[0] == '0');
+ serverBans[index].subnet = atoi(maskpos);
+
+ if(serverBans[index].ip.type == NA_IP &&
+ (serverBans[index].subnet < 0 || serverBans[index].subnet > 32))
+ {
+ serverBans[index].subnet = 32;
+ }
+ else if(serverBans[index].ip.type == NA_IP6 &&
+ (serverBans[index].subnet < 0 || serverBans[index].subnet > 128))
+ {
+ serverBans[index].subnet = 128;
+ }
+ }
+
+ curpos = newlinepos + 1;
+ }
+
+ serverBansCount = index;
+
+ Z_Free(textbuf);
+ }
+}
+
+/*
+==================
+SV_BanAddr_f
+
+Ban a user from being able to play on this server based on his ip address.
+==================
+*/
+
+static void SV_AddBanToList(qboolean isexception)
+{
+ char *banstring, *suffix;
+ netadr_t ip;
+ int argc, mask;
+ fileHandle_t writeto;
+
+ argc = Cmd_Argc();
+
+ if(argc < 2 || argc > 3)
+ {
+ Com_Printf ("Usage: %s (ip[/subnet] | clientnum [subnet])\n", Cmd_Argv(0));
+ return;
+ }
+
+ if(serverBansCount > sizeof(serverBans) / sizeof(*serverBans))
+ {
+ Com_Printf ("Error: Maximum number of bans/exceptions exceeded.\n");
+ return;
+ }
+
+ banstring = Cmd_Argv(1);
+
+ if(strchr(banstring, '.') || strchr(banstring, ':'))
+ {
+ // This is an ip address, not a client num.
+
+ // Look for a CIDR-Notation suffix
+ suffix = strchr(banstring, '/');
+ if(suffix)
+ {
+ *suffix = '\0';
+ suffix++;
+ }
+
+ if(!NET_StringToAdr(banstring, &ip, NA_UNSPEC))
+ {
+ Com_Printf("Error: Invalid address %s\n", banstring);
+ return;
+ }
+ }
+ else
+ {
+ client_t *cl;
+
+ // client num.
+ if(!com_sv_running->integer)
+ {
+ Com_Printf("Server is not running.\n");
+ return;
+ }
+
+ cl = SV_GetPlayerByNum();
+
+ if(!cl)
+ {
+ Com_Printf("Error: Playernum %s does not exist.\n", Cmd_Argv(1));
+ return;
+ }
+
+ ip = cl->netchan.remoteAddress;
+
+ if(argc == 3)
+ suffix = Cmd_Argv(2);
+ else
+ suffix = NULL;
+ }
+
+ if(ip.type != NA_IP && ip.type != NA_IP6)
+ {
+ Com_Printf("Error: Can ban players connected via the internet only.\n");
+ return;
+ }
+
+ if(suffix)
+ {
+ mask = atoi(suffix);
+
+ if(ip.type == NA_IP)
+ {
+ if(mask < 0 || mask > 32)
+ mask = 32;
+ }
+ else
+ {
+ if(mask < 0 || mask > 128)
+ mask = 128;
+ }
+ }
+ else if(ip.type == NA_IP)
+ mask = 32;
+ else
+ mask = 128;
+
+ serverBans[serverBansCount].ip = ip;
+ serverBans[serverBansCount].subnet = mask;
+ serverBans[serverBansCount].isexception = isexception;
+
+ Com_Printf("Added %s: %s/%d\n", isexception ? "ban exception" : "ban",
+ NET_AdrToString(ip), mask);
+
+ // Write out the ban information.
+ if((writeto = FS_FOpenFileAppend(SERVER_BANFILE)))
+ {
+ char writebuf[128];
+
+ Com_sprintf(writebuf, sizeof(writebuf), "%d %s %d\n", isexception, NET_AdrToString(ip), mask);
+ FS_Write(writebuf, strlen(writebuf), writeto);
+ FS_FCloseFile(writeto);
+ }
+
+ serverBansCount++;
+}
+
+static void SV_DelBanFromList(qboolean isexception)
+{
+ int index, count, todel;
+ fileHandle_t writeto;
+
+ if(Cmd_Argc() != 2)
+ {
+ Com_Printf ("Usage: %s <num>\n", Cmd_Argv(0));
+ return;
+ }
+
+ todel = atoi(Cmd_Argv(1));
+
+ if(todel < 0 || todel > serverBansCount)
+ return;
+
+ for(index = count = 0; index < serverBansCount; index++)
+ {
+ if(serverBans[index].isexception == isexception)
+ {
+ count++;
+
+ if(count == todel)
+ break;
+ }
+ }
+
+ if(index == serverBansCount - 1)
+ serverBansCount--;
+ else if(index < sizeof(serverBans) / sizeof(*serverBans) - 1)
+ {
+ memmove(serverBans + index, serverBans + index + 1, (serverBansCount - index - 1) * sizeof(*serverBans));
+ serverBansCount--;
+ }
+ else
+ {
+ Com_Printf("Error: No such entry #%d\n", todel);
+ return;
+ }
+
+ // Write out the ban information.
+ if((writeto = FS_FOpenFileWrite(SERVER_BANFILE)))
+ {
+ char writebuf[128];
+ serverBan_t *curban;
+
+ for(index = 0; index < serverBansCount; index++)
+ {
+ curban = &serverBans[index];
+
+ Com_sprintf(writebuf, sizeof(writebuf), "%d %s %d\n",
+ curban->isexception, NET_AdrToString(curban->ip), curban->subnet);
+ FS_Write(writebuf, strlen(writebuf), writeto);
+ }
+
+ FS_FCloseFile(writeto);
+ }
+}
+
+static void SV_ListBans_f(void)
+{
+ int index, count;
+ serverBan_t *ban;
+
+ // List all bans
+ for(index = count = 0; index < serverBansCount; index++)
+ {
+ ban = &serverBans[index];
+ if(!ban->isexception)
+ {
+ count++;
+
+ Com_Printf("Ban #%d: %s/%d\n", count,
+ NET_AdrToString(ban->ip), ban->subnet);
+ }
+ }
+ // List all exceptions
+ for(index = count = 0; index < serverBansCount; index++)
+ {
+ ban = &serverBans[index];
+ if(ban->isexception)
+ {
+ count++;
+
+ Com_Printf("Except #%d: %s/%d\n", count,
+ NET_AdrToString(ban->ip), ban->subnet);
+ }
+ }
+}
+
+static void SV_FlushBans_f(void)
+{
+ fileHandle_t blankf;
+
+ serverBansCount = 0;
+
+ // empty the ban file.
+ blankf = FS_FOpenFileWrite(SERVER_BANFILE);
+
+ if(blankf)
+ FS_FCloseFile(blankf);
+}
+
+static void SV_BanAddr_f(void)
+{
+ SV_AddBanToList(qfalse);
+}
+
+static void SV_ExceptAddr_f(void)
+{
+ SV_AddBanToList(qtrue);
+}
+
+static void SV_BanDel_f(void)
+{
+ SV_DelBanFromList(qfalse);
+}
+
+static void SV_ExceptDel_f(void)
+{
+ SV_DelBanFromList(qtrue);
+}
+
+/*
+==================
SV_KickNum_f
Kick a user off of the server FIXME: move to game
@@ -763,6 +1090,14 @@ void SV_AddOperatorCommands( void ) {
if( com_dedicated->integer ) {
Cmd_AddCommand ("say", SV_ConSay_f);
}
+
+ Cmd_AddCommand("rehashbans", SV_RehashBans_f);
+ Cmd_AddCommand("listbans", SV_ListBans_f);
+ Cmd_AddCommand("banaddr", SV_BanAddr_f);
+ Cmd_AddCommand("exceptaddr", SV_ExceptAddr_f);
+ Cmd_AddCommand("bandel", SV_BanDel_f);
+ Cmd_AddCommand("exceptdel", SV_ExceptDel_f);
+ Cmd_AddCommand("flushbans", SV_FlushBans_f);
}
/*
diff --git a/code/server/sv_client.c b/code/server/sv_client.c
index 8230036..eced3d9 100644
--- a/code/server/sv_client.c
+++ b/code/server/sv_client.c
@@ -235,6 +235,86 @@ A "connect" OOB command has been received
==================
*/
+qboolean SV_IsBanned(netadr_t *from, qboolean isexception)
+{
+ int index, addrlen, curbyte, netmask, cmpmask;
+ serverBan_t *curban;
+ byte *addrfrom, *addrban;
+ qboolean differed;
+
+ if(from->type == NA_IP)
+ addrlen = sizeof(from->ip);
+ else if(from->type == NA_IP6)
+ addrlen = sizeof(from->ip6);
+ else
+ return qfalse;
+
+ if(!isexception)
+ {
+ // If this is a query for a ban, first check whether the client is excepted
+ if(SV_IsBanned(from, qtrue))
+ return qfalse;
+ }
+
+ for(index = 0; index < serverBansCount; index++)
+ {
+ curban = &serverBans[index];
+
+ if(curban->isexception == isexception && from->type == curban->ip.type)
+ {
+ if(from->type == NA_IP)
+ {
+ addrfrom = from->ip;
+ addrban = curban->ip.ip;
+ }
+ else
+ {
+ addrfrom = from->ip6;
+ addrban = curban->ip.ip6;
+ }
+
+ differed = qfalse;
+ curbyte = 0;
+
+ for(netmask = curban->subnet; netmask > 7; netmask -= 8)
+ {
+ if(addrfrom[curbyte] != addrban[curbyte])
+ {
+ differed = qtrue;
+ break;
+ }
+
+ curbyte++;
+ }
+
+ if(differed)
+ continue;
+
+ if(netmask)
+ {
+ cmpmask = (1 << netmask) - 1;
+ cmpmask <<= 8 - netmask;
+
+ if((addrfrom[curbyte] & cmpmask) == (addrban[curbyte] & cmpmask))
+ return qtrue;
+ }
+ else
+ return qtrue;
+
+ }
+ }
+
+ return qfalse;
+}
+
+/*
+==================
+SV_DirectConnect
+
+A "connect" OOB command has been received
+==================
+*/
+
void SV_DirectConnect( netadr_t from ) {
char userinfo[MAX_INFO_STRING];
int i;
@@ -252,6 +332,13 @@ void SV_DirectConnect( netadr_t from ) {
char *ip;
Com_DPrintf ("SVC_DirectConnect ()\n");
+
+ // Check whether this client is banned.
+ if(SV_IsBanned(&from, qfalse))
+ {
+ NET_OutOfBandPrint(NS_SERVER, from, "print\nYou are banned from this server.\n");
+ return;
+ }
Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) );
@@ -821,11 +908,13 @@ void SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
}
}
+ cl->download = 0;
+
// We open the file here
if ( !(sv_allowDownload->integer & DLF_ENABLE) ||
(sv_allowDownload->integer & DLF_NO_UDP) ||
idPack || unreferenced ||
- ( cl->downloadSize = FS_SV_FOpenFileRead( cl->downloadName, &cl->download ) ) <= 0 ) {
+ ( cl->downloadSize = FS_SV_FOpenFileRead( cl->downloadName, &cl->download ) ) < 0 ) {
// cannot auto-download file
if(unreferenced)
{
@@ -868,6 +957,10 @@ void SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
MSG_WriteString( msg, errorMessage );
*cl->downloadName = 0;
+
+ if(cl->download)
+ FS_FCloseFile(cl->download);
+
return;
}
diff --git a/code/server/sv_init.c b/code/server/sv_init.c
index 3b9cf3d..aca7691 100644
--- a/code/server/sv_init.c
+++ b/code/server/sv_init.c
@@ -687,6 +687,9 @@ void SV_Init (void) {
// init the botlib here because we need the pre-compiler in the UI
SV_BotInitBotLib();
+
+ // Load saved bans
+ Cbuf_AddText("rehashbans\n");
}
diff --git a/code/server/sv_main.c b/code/server/sv_main.c
index 96a6515..552c12b 100644
--- a/code/server/sv_main.c
+++ b/code/server/sv_main.c
@@ -54,6 +54,9 @@ cvar_t *sv_floodProtect;
cvar_t *sv_lanForceRate; // dedicated 1 (LAN) server forces local client rates to 99999 (bug #491)
cvar_t *sv_strictAuth;
+serverBan_t serverBans[SERVER_MAXBANS];
+int serverBansCount = 0;
+
/*
=============================================================================