From 1dc7e94283f026b17c1e793cbf7542872812ffda Mon Sep 17 00:00:00 2001 From: Tim Angus Date: Wed, 28 Dec 2005 02:32:43 +0000 Subject: * Added master server to source, based on dpmaster * Removed gametype, fraglimit and dmflags cvars * Removed CD key authentication stuff * Implemented a means to save and restore cmd context * Bumped protocol version up to 69 (same as 68) * Removed various references to punkbuster * Maps on create server menu now sorted by name * Fixed some warnings --- src/client/cl_console.c | 4 + src/client/cl_main.c | 215 ++------------- src/client/cl_ui.c | 60 ----- src/client/client.h | 2 - src/game/bg_public.h | 2 +- src/game/g_local.h | 3 - src/game/g_main.c | 6 +- src/master/Makefile | 52 ++++ src/master/common.h | 88 +++++++ src/master/master.c | 657 ++++++++++++++++++++++++++++++++++++++++++++++ src/master/messages.c | 526 +++++++++++++++++++++++++++++++++++++ src/master/messages.h | 35 +++ src/master/servers.c | 666 +++++++++++++++++++++++++++++++++++++++++++++++ src/master/servers.h | 102 ++++++++ src/null/null_client.c | 3 - src/qcommon/cm_trace.c | 3 +- src/qcommon/cmd.c | 93 ++++--- src/qcommon/common.c | 72 +---- src/qcommon/cvar.c | 3 - src/qcommon/files.c | 10 - src/qcommon/q_shared.h | 4 - src/qcommon/qcommon.h | 22 +- src/server/server.h | 1 - src/server/sv_ccmds.c | 11 +- src/server/sv_client.c | 140 +--------- src/server/sv_init.c | 6 - src/server/sv_main.c | 6 +- src/server/sv_rankings.c | 2 +- src/ui/ui_atoms.c | 5 - src/ui/ui_gameinfo.c | 23 +- src/ui/ui_local.h | 13 - src/ui/ui_main.c | 75 +----- src/ui/ui_public.h | 10 +- src/ui/ui_syscalls.asm | 3 - src/ui/ui_syscalls.c | 12 - src/unix/Makefile | 10 +- 36 files changed, 2266 insertions(+), 679 deletions(-) create mode 100644 src/master/Makefile create mode 100644 src/master/common.h create mode 100644 src/master/master.c create mode 100644 src/master/messages.c create mode 100644 src/master/messages.h create mode 100644 src/master/servers.c create mode 100644 src/master/servers.h (limited to 'src') diff --git a/src/client/cl_console.c b/src/client/cl_console.c index d80bff0f..efbf2d17 100644 --- a/src/client/cl_console.c +++ b/src/client/cl_console.c @@ -378,9 +378,13 @@ void CL_ConsolePrint( char *txt ) { } if( !skipnotify && !( cls.keyCatchers & KEYCATCH_CONSOLE ) ) { + Cmd_SaveCmdContext( ); + // feed the text to cgame Cmd_TokenizeString( txt ); CL_GameConsoleText( ); + + Cmd_RestoreCmdContext( ); } color = ColorIndex(COLOR_WHITE); diff --git a/src/client/cl_main.c b/src/client/cl_main.c index bb322b6a..0789ac9b 100644 --- a/src/client/cl_main.c +++ b/src/client/cl_main.c @@ -820,116 +820,31 @@ void CL_RequestMotd( void ) { if ( !cl_motd->integer ) { return; } - Com_Printf( "Resolving %s\n", UPDATE_SERVER_NAME ); - if ( !NET_StringToAdr( UPDATE_SERVER_NAME, &cls.updateServer ) ) { + Com_Printf( "Resolving %s\n", MASTER_SERVER_NAME ); + if ( !NET_StringToAdr( MASTER_SERVER_NAME, &cls.updateServer ) ) { Com_Printf( "Couldn't resolve address\n" ); return; } - cls.updateServer.port = BigShort( PORT_UPDATE ); - Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", UPDATE_SERVER_NAME, + cls.updateServer.port = BigShort( PORT_MASTER ); + Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", MASTER_SERVER_NAME, cls.updateServer.ip[0], cls.updateServer.ip[1], cls.updateServer.ip[2], cls.updateServer.ip[3], BigShort( cls.updateServer.port ) ); - + info[0] = 0; // NOTE TTimo xoring against Com_Milliseconds, otherwise we may not have a true randomization // only srand I could catch before here is tr_noise.c l:26 srand(1001) // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=382 // NOTE: the Com_Milliseconds xoring only affects the lower 16-bit word, // but I decided it was enough randomization - Com_sprintf( cls.updateChallenge, sizeof( cls.updateChallenge ), "%i", ((rand() << 16) ^ rand()) ^ Com_Milliseconds()); + Com_sprintf( cls.updateChallenge, sizeof( cls.updateChallenge ), + "%i", ((rand() << 16) ^ rand()) ^ Com_Milliseconds()); Info_SetValueForKey( info, "challenge", cls.updateChallenge ); Info_SetValueForKey( info, "renderer", cls.glconfig.renderer_string ); Info_SetValueForKey( info, "version", com_version->string ); - NET_OutOfBandPrint( NS_CLIENT, cls.updateServer, "getmotd \"%s\"\n", info ); -} - -/* -=================== -CL_RequestAuthorization - -Authorization server protocol ------------------------------ - -All commands are text in Q3 out of band packets (leading 0xff 0xff 0xff 0xff). - -Whenever the client tries to get a challenge from the server it wants to -connect to, it also blindly fires off a packet to the authorize server: - -getKeyAuthorize - -cdkey may be "demo" - - -#OLD The authorize server returns a: -#OLD -#OLD keyAthorize -#OLD -#OLD A client will be accepted if the cdkey is valid and it has not been used by any other IP -#OLD address in the last 15 minutes. - - -The server sends a: - -getIpAuthorize - -The authorize server returns a: - -ipAuthorize - -A client will be accepted if a valid cdkey was sent by that ip (only) in the last 15 minutes. -If no response is received from the authorize server after two tries, the client will be let -in anyway. -=================== -*/ -void CL_RequestAuthorization( void ) { - char nums[64]; - int i, j, l; - cvar_t *fs; - - if ( !cls.authorizeServer.port ) { - Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); - if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &cls.authorizeServer ) ) { - Com_Printf( "Couldn't resolve address\n" ); - return; - } - - cls.authorizeServer.port = BigShort( PORT_AUTHORIZE ); - Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, - cls.authorizeServer.ip[0], cls.authorizeServer.ip[1], - cls.authorizeServer.ip[2], cls.authorizeServer.ip[3], - BigShort( cls.authorizeServer.port ) ); - } - if ( cls.authorizeServer.type == NA_BAD ) { - return; - } - - if ( Cvar_VariableValue( "fs_restrict" ) ) { - Q_strncpyz( nums, "demota", sizeof( nums ) ); - } else { - // only grab the alphanumeric values from the cdkey, to avoid any dashes or spaces - j = 0; - l = strlen( cl_cdkey ); - if ( l > 32 ) { - l = 32; - } - for ( i = 0 ; i < l ; i++ ) { - if ( ( cl_cdkey[i] >= '0' && cl_cdkey[i] <= '9' ) - || ( cl_cdkey[i] >= 'a' && cl_cdkey[i] <= 'z' ) - || ( cl_cdkey[i] >= 'A' && cl_cdkey[i] <= 'Z' ) - ) { - nums[j] = cl_cdkey[i]; - j++; - } - } - nums[j] = 0; - } - - fs = Cvar_Get ("cl_anonymous", "0", CVAR_INIT|CVAR_SYSTEMINFO ); - - NET_OutOfBandPrint(NS_CLIENT, cls.authorizeServer, va("getKeyAuthorize %i %s", fs->integer, nums) ); + NET_OutOfBandPrint( NS_CLIENT, cls.updateServer, "getmotd%s", info ); } /* @@ -1519,9 +1434,6 @@ void CL_CheckForResend( void ) { switch ( cls.state ) { case CA_CONNECTING: // requesting a challenge - if ( !Sys_IsLANAddress( clc.serverAddress ) ) { - CL_RequestAuthorization(); - } NET_OutOfBandPrint(NS_CLIENT, clc.serverAddress, "getchallenge"); break; @@ -1596,27 +1508,24 @@ CL_MotdPacket =================== */ -void CL_MotdPacket( netadr_t from ) { - char *challenge; - char *info; +void CL_MotdPacket( netadr_t from, const char *info ) { + char *v; // if not from our server, ignore it if ( !NET_CompareAdr( from, cls.updateServer ) ) { return; } - info = Cmd_Argv(1); - // check challenge - challenge = Info_ValueForKey( info, "challenge" ); - if ( strcmp( challenge, cls.updateChallenge ) ) { + v = Info_ValueForKey( info, "challenge" ); + if ( strcmp( v, cls.updateChallenge ) ) { return; } - challenge = Info_ValueForKey( info, "motd" ); + v = Info_ValueForKey( info, "motd" ); Q_strncpyz( cls.updateInfoString, info, sizeof( cls.updateInfoString ) ); - Cvar_Set( "cl_motdString", challenge ); + Cvar_Set( "cl_motdString", v ); } /* @@ -1706,7 +1615,7 @@ void CL_ServersResponsePacket( netadr_t from, msg_t *msg ) { addresses[numservers].ip[1], addresses[numservers].ip[2], addresses[numservers].ip[3], - addresses[numservers].port ); + BigShort( addresses[numservers].port ) ); numservers++; if (numservers >= MAX_SERVERSPERPACKET) { @@ -1773,7 +1682,8 @@ Responses to broadcasts, etc */ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { char *s; - char *c; + char c[ BIG_INFO_STRING ]; + char arg1[ BIG_INFO_STRING ]; MSG_BeginReadingOOB( msg ); MSG_ReadLong( msg ); // skip the -1 @@ -1782,7 +1692,8 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { Cmd_TokenizeString( s ); - c = Cmd_Argv(0); + Q_strncpyz( c, Cmd_Argv( 0 ), BIG_INFO_STRING ); + Q_strncpyz( arg1, Cmd_Argv( 1 ), BIG_INFO_STRING ); Com_DPrintf ("CL packet %s: %s\n", NET_AdrToString(from), c); @@ -1792,7 +1703,7 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { Com_Printf( "Unwanted challenge response received. Ignored.\n" ); } else { // start sending challenge repsonse instead of challenge request packets - clc.challenge = atoi(Cmd_Argv(1)); + clc.challenge = atoi(arg1); cls.state = CA_CHALLENGING; clc.connectPacketCount = 0; clc.connectTime = -99999; @@ -1848,19 +1759,13 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { // echo request from server if ( !Q_stricmp(c, "echo") ) { - NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) ); + NET_OutOfBandPrint( NS_CLIENT, from, "%s", arg1 ); return; } - // cd check - if ( !Q_stricmp(c, "keyAuthorize") ) { - // we don't use these now, so dump them on the floor - return; - } - - // global MOTD from id + // global MOTD from trem master if ( !Q_stricmp(c, "motd") ) { - CL_MotdPacket( from ); + CL_MotdPacket( from, arg1 ); return; } @@ -2003,11 +1908,7 @@ void CL_Frame ( int msec ) { return; } - if ( cls.cddialog ) { - // bring up the cd error dialog if needed - cls.cddialog = qfalse; - VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NEED_CD ); - } else if ( cls.state == CA_DISCONNECTED && !( cls.keyCatchers & KEYCATCH_UI ) + if ( cls.state == CA_DISCONNECTED && !( cls.keyCatchers & KEYCATCH_UI ) && !com_sv_running->integer ) { // if disconnected, bring up the menu S_StopAllSounds(); @@ -2474,7 +2375,6 @@ static void CL_SetServerInfo(serverInfo_t *server, const char *info, int ping) { server->netType = atoi(Info_ValueForKey(info, "nettype")); server->minPing = atoi(Info_ValueForKey(info, "minping")); server->maxPing = atoi(Info_ValueForKey(info, "maxping")); - server->punkbuster = atoi(Info_ValueForKey(info, "punkbuster")); } server->ping = ping; } @@ -2605,7 +2505,6 @@ void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) { cls.localServers[i].game[0] = '\0'; cls.localServers[i].gameType = 0; cls.localServers[i].netType = from.type; - cls.localServers[i].punkbuster = 0; Q_strncpyz( info, MSG_ReadString( msg ), MAX_INFO_STRING ); if (strlen(info)) { @@ -3260,69 +3159,3 @@ CL_ShowIP_f void CL_ShowIP_f(void) { Sys_ShowIP(); } - -/* -================= -bool CL_CDKeyValidate -================= -*/ -qboolean CL_CDKeyValidate( const char *key, const char *checksum ) { - char ch; - byte sum; - char chs[3]; - int i, len; - - len = strlen(key); - if( len != CDKEY_LEN ) { - return qfalse; - } - - if( checksum && strlen( checksum ) != CDCHKSUM_LEN ) { - return qfalse; - } - - sum = 0; - // for loop gets rid of conditional assignment warning - for (i = 0; i < len; i++) { - ch = *key++; - if (ch>='a' && ch<='z') { - ch -= 32; - } - switch( ch ) { - case '2': - case '3': - case '7': - case 'A': - case 'B': - case 'C': - case 'D': - case 'G': - case 'H': - case 'J': - case 'L': - case 'P': - case 'R': - case 'S': - case 'T': - case 'W': - sum += ch; - continue; - default: - return qfalse; - } - } - - sprintf(chs, "%02x", sum); - - if (checksum && !Q_stricmp(chs, checksum)) { - return qtrue; - } - - if (!checksum) { - return qtrue; - } - - return qfalse; -} - - diff --git a/src/client/cl_ui.c b/src/client/cl_ui.c index 24186379..7351d77e 100644 --- a/src/client/cl_ui.c +++ b/src/client/cl_ui.c @@ -322,7 +322,6 @@ static void LAN_GetServerInfo( int source, int n, char *buf, int buflen ) { Info_SetValueForKey( info, "gametype", va("%i",server->gameType)); Info_SetValueForKey( info, "nettype", va("%i",server->netType)); Info_SetValueForKey( info, "addr", NET_AdrToString(server->adr)); - Info_SetValueForKey( info, "punkbuster", va("%i", server->punkbuster)); Q_strncpyz(buf, info, buflen); } else { if (buf) { @@ -671,44 +670,6 @@ void Key_SetCatcher( int catcher ) { } -/* -==================== -CLUI_GetCDKey -==================== -*/ -static void CLUI_GetCDKey( char *buf, int buflen ) { - cvar_t *fs; - fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); - if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) { - Com_Memcpy( buf, &cl_cdkey[16], 16); - buf[16] = 0; - } else { - Com_Memcpy( buf, cl_cdkey, 16); - buf[16] = 0; - } -} - - -/* -==================== -CLUI_SetCDKey -==================== -*/ -static void CLUI_SetCDKey( char *buf ) { - cvar_t *fs; - fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); - if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) { - Com_Memcpy( &cl_cdkey[16], buf, 16 ); - cl_cdkey[32] = 0; - // set the flag so the fle will be written at the next opportunity - cvar_modifiedFlags |= CVAR_ARCHIVE; - } else { - Com_Memcpy( cl_cdkey, buf, 16 ); - // set the flag so the fle will be written at the next opportunity - cvar_modifiedFlags |= CVAR_ARCHIVE; - } -} - /* ==================== GetConfigString @@ -1005,14 +966,6 @@ long CL_UISystemCalls( long *args ) { case UI_MEMORY_REMAINING: return Hunk_MemoryRemaining(); - case UI_GET_CDKEY: - CLUI_GetCDKey( VMA(1), args[2] ); - return 0; - - case UI_SET_CDKEY: - CLUI_SetCDKey( VMA(1) ); - return 0; - case UI_SET_PBCLSTATUS: return 0; @@ -1093,11 +1046,6 @@ long CL_UISystemCalls( long *args ) { re.RemapShader( VMA(1), VMA(2), VMA(3) ); return 0; - case UI_VERIFY_CDKEY: - return CL_CDKeyValidate(VMA(1), VMA(2)); - - - default: Com_Error( ERR_DROP, "Bad UI system trap: %i", args[0] ); @@ -1162,14 +1110,6 @@ void CL_InitUI( void ) { } } -qboolean UI_usesUniqueCDKey( void ) { - if (uivm) { - return (VM_Call( uivm, UI_HASUNIQUECDKEY) == qtrue); - } else { - return qfalse; - } -} - /* ==================== UI_GameCommand diff --git a/src/client/client.h b/src/client/client.h index eff15d51..e597047f 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -239,7 +239,6 @@ typedef struct { int maxPing; int ping; qboolean visible; - int punkbuster; } serverInfo_t; typedef struct { @@ -382,7 +381,6 @@ int CL_GetPingQueueCount( void ); void CL_ShutdownRef( void ); void CL_InitRef( void ); -qboolean CL_CDKeyValidate( const char *key, const char *checksum ); int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen ); diff --git a/src/game/bg_public.h b/src/game/bg_public.h index 2b713951..6bcb0d4e 100644 --- a/src/game/bg_public.h +++ b/src/game/bg_public.h @@ -28,7 +28,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // because games can change separately from the main system version, we need a // second version that must match between game and cgame -#define GAME_VERSION "tremulous" +#define GAME_VERSION "base" #define DEFAULT_GRAVITY 800 diff --git a/src/game/g_local.h b/src/game/g_local.h index 0d38cf02..4431edaf 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -29,9 +29,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA //================================================================== -// the "gameversion" client command will print this plus compile date -#define GAMEVERSION "tremulous" - #define INFINITE 1000000 #define FRAMETIME 100 // msec diff --git a/src/game/g_main.c b/src/game/g_main.c index 95cbda14..e77b5b76 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -112,7 +112,7 @@ static cvarTable_t gameCvarTable[ ] = { &g_cheats, "sv_cheats", "", 0, 0, qfalse }, // noset vars - { NULL, "gamename", GAMEVERSION , CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, + { NULL, "gamename", GAME_VERSION , CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, { NULL, "gamedate", __DATE__ , CVAR_ROM, 0, qfalse }, { &g_restarted, "g_restarted", "0", CVAR_ROM, 0, qfalse }, { NULL, "sv_mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, @@ -545,10 +545,8 @@ void G_InitGame( int levelTime, int randomSeed, int restart ) G_RegisterCvars( ); - //TA: moved after G_RegisterCvars since G_Printf - // now depends on the value of g_dedicated G_Printf( "------- Game Initialization -------\n" ); - G_Printf( "gamename: %s\n", GAMEVERSION ); + G_Printf( "gamename: %s\n", GAME_VERSION ); G_Printf( "gamedate: %s\n", __DATE__ ); G_ProcessIPBans( ); diff --git a/src/master/Makefile b/src/master/Makefile new file mode 100644 index 00000000..308ebf05 --- /dev/null +++ b/src/master/Makefile @@ -0,0 +1,52 @@ +ARCH=$(shell uname -m | sed -e s/i.86/i386/) +PLATFORM=$(shell uname|sed -e s/_.*//|tr A-Z a-z) +BD_DEBUG=debug-$(ARCH)$(PLATFORM) +BD_RELEASE=release-$(ARCH)$(PLATFORM) + +ifeq ($(PLATFORM),mingw32) + BINEXT=.exe + RELEASE_LDFLAGS=-lwsock32 + DEBUG_LDFLAGS=-lwsock32 + RM=del + MKDIR=md +else + BINEXT= + RELEASE_LDFLAGS= + DEBUG_LDFLAGS= + RM=rm -f + MKDIR=mkdir +endif + +CC=gcc +RELEASE_CFLAGS=-Wall -O2 +DEBUG_CFLAGS=-g +OBJECTS= \ + $(BD)/master.o \ + $(BD)/messages.o \ + $(BD)/servers.o + +release: makedirs + $(MAKE) $(BD_RELEASE)/tremmaster BD=$(BD_RELEASE) \ + CFLAGS="$(CFLAGS) $(RELEASE_CFLAGS)" LDFLAGS="$(LDFLAGS) $(RELEASE_LDFLAGS)" + +debug: makedirs + $(MAKE) $(BD_DEBUG)/tremmaster BD=$(BD_DEBUG) \ + CFLAGS="$(CFLAGS) $(DEBUG_CFLAGS)" LDFLAGS="$(LDFLAGS) $(DEBUG_LDFLAGS)" + +all: release debug + +$(BD)/%.o: %.c + $(CC) $(CFLAGS) -o $@ -c $< + +$(BD)/tremmaster: $(OBJECTS) + $(CC) -o $@ $(OBJECTS) $(LDFLAGS) + +clean: + -$(RM) $(BD_DEBUG)/* + -$(RM) $(BD_RELEASE)/* + +makedirs: + @if [ ! -d $(BD_RELEASE) ];then $(MKDIR) $(BD_RELEASE);fi + @if [ ! -d $(BD_DEBUG) ];then $(MKDIR) $(BD_DEBUG);fi + +.PHONY: all clean diff --git a/src/master/common.h b/src/master/common.h new file mode 100644 index 00000000..e38aa039 --- /dev/null +++ b/src/master/common.h @@ -0,0 +1,88 @@ +/* + common.h + + Common header file for dpmaster + + Copyright (C) 2004-2005 Mathieu Olivier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef _COMMON_H_ +#define _COMMON_H_ + + +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +# include +#else +# include +# include +# include +# include +#endif + + +// ---------- Types ---------- // + +// A few basic types +typedef enum {qfalse, qtrue} qboolean; +typedef unsigned char qbyte; + +// The various messages levels +typedef enum +{ + MSG_NOPRINT, // used by "max_msg_level" (= no printings) + MSG_ERROR, // errors + MSG_WARNING, // warnings + MSG_NORMAL, // standard messages + MSG_DEBUG // for debugging purpose +} msg_level_t; + + +// ---------- Public variables ---------- // + +// The master socket +extern int sock; + +// The current time (updated every time we receive a packet) +extern time_t crt_time; + +// Maximum level for a message to be printed +extern msg_level_t max_msg_level; + +// Peer address. We rebuild it every time we receive a new packet +extern char peer_address [128]; + + +// ---------- Public functions ---------- // + +// Win32 uses a different name for some standard functions +#ifdef WIN32 +# define snprintf _snprintf +#endif + +// Print a message to screen, depending on its verbose level +int MsgPrint (msg_level_t msg_level, const char* format, ...); + + +#endif // #ifndef _COMMON_H_ diff --git a/src/master/master.c b/src/master/master.c new file mode 100644 index 00000000..1158e59e --- /dev/null +++ b/src/master/master.c @@ -0,0 +1,657 @@ +/* + master.c + + A master server for Tremulous + + Copyright (C) 2002-2005 Mathieu Olivier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include +#include +#include + +#ifndef WIN32 +# include +# include +#endif + +#include "common.h" +#include "messages.h" +#include "servers.h" + + +// ---------- Constants ---------- // + +// Version of dpmaster +#define VERSION "1.6" + +// Default master port +#define DEFAULT_MASTER_PORT 27950 + +// Maximum and minimum sizes for a valid packet +#define MAX_PACKET_SIZE 2048 +#define MIN_PACKET_SIZE 5 + +#ifndef WIN32 +// Default path we use for chroot +# define DEFAULT_JAIL_PATH "/var/empty/" + +// User we use by default for dropping super-user privileges +# define DEFAULT_LOW_PRIV_USER "nobody" +#endif + + +// ---------- Types ---------- // + +#ifdef WIN32 +typedef int socklen_t; +#endif + + +// ---------- Private variables ---------- // + +// The port we use +static unsigned short master_port = DEFAULT_MASTER_PORT; + +// Local address we listen on, if any +static const char* listen_name = NULL; +static struct in_addr listen_addr; + +#ifndef WIN32 +// On UNIX systems, we can run as a daemon +static qboolean daemon_mode = qfalse; + +// Path we use for chroot +static const char* jail_path = DEFAULT_JAIL_PATH; + +// Low privileges user +static const char* low_priv_user = DEFAULT_LOW_PRIV_USER; +#endif + + +// ---------- Public variables ---------- // + +// The master socket +int sock = -1; + +// The current time (updated every time we receive a packet) +time_t crt_time; + +// Maximum level for a message to be printed +msg_level_t max_msg_level = MSG_NORMAL; + +// Peer address. We rebuild it every time we receive a new packet +char peer_address [128]; + + +// ---------- Private functions ---------- // + +/* +==================== +PrintPacket + +Print the contents of a packet on stdout +==================== +*/ +static void PrintPacket (const char* packet, size_t length) +{ + size_t i; + + // Exceptionally, we use MSG_NOPRINT here because if the function is + // called, the user probably wants this text to be displayed + // whatever the maximum message level is. + MsgPrint (MSG_NOPRINT, "\""); + + for (i = 0; i < length; i++) + { + char c = packet[i]; + if (c == '\\') + MsgPrint (MSG_NOPRINT, "\\\\"); + else if (c == '\"') + MsgPrint (MSG_NOPRINT, "\""); + else if (c >= 32 && (qbyte)c <= 127) + MsgPrint (MSG_NOPRINT, "%c", c); + else + MsgPrint (MSG_NOPRINT, "\\x%02X", c); + } + + MsgPrint (MSG_NOPRINT, "\" (%u bytes)\n", length); +} + + +/* +==================== +SysInit + +System dependent initializations +==================== +*/ +static qboolean SysInit (void) +{ +#ifdef WIN32 + WSADATA winsockdata; + + if (WSAStartup (MAKEWORD (1, 1), &winsockdata)) + { + MsgPrint (MSG_ERROR, "ERROR: can't initialize winsocks\n"); + return qfalse; + } +#endif + + return qtrue; +} + + +/* +==================== +UnsecureInit + +System independent initializations, called BEFORE the security initializations. +We need this intermediate step because DNS requests may not be able to resolve +after the security initializations, due to chroot. +==================== +*/ +static qboolean UnsecureInit (void) +{ + // Resolve the address mapping list + if (! Sv_ResolveAddressMappings ()) + return qfalse; + + // Resolve the listen address if one was specified + if (listen_name != NULL) + { + struct hostent* itf; + + itf = gethostbyname (listen_name); + if (itf == NULL) + { + MsgPrint (MSG_ERROR, "ERROR: can't resolve %s\n", listen_name); + return qfalse; + } + if (itf->h_addrtype != AF_INET) + { + MsgPrint (MSG_ERROR, "ERROR: %s is not an IPv4 address\n", + listen_name); + return qfalse; + } + + memcpy (&listen_addr.s_addr, itf->h_addr, + sizeof (listen_addr.s_addr)); + } + + return qtrue; +} + + +/* +==================== +SecInit + +Security initializations (system dependent) +==================== +*/ +static qboolean SecInit (void) +{ +#ifndef WIN32 + // Should we run as a daemon? + if (daemon_mode && daemon (0, 0)) + { + MsgPrint (MSG_NOPRINT, "ERROR: daemonization failed (%s)\n", + strerror (errno)); + return qfalse; + } + + // UNIX allows us to be completely paranoid, so let's go for it + if (geteuid () == 0) + { + struct passwd* pw; + + MsgPrint (MSG_WARNING, + "WARNING: running with super-user privileges\n"); + + // We must get the account infos before the calls to chroot and chdir + pw = getpwnam (low_priv_user); + if (pw == NULL) + { + MsgPrint (MSG_ERROR, "ERROR: can't get user \"%s\" properties\n", + low_priv_user); + return qfalse; + } + + // Chroot ourself + MsgPrint (MSG_NORMAL, " - chrooting myself to %s... ", jail_path); + if (chroot (jail_path) || chdir ("/")) + { + MsgPrint (MSG_ERROR, "FAILED (%s)\n", strerror (errno)); + return qfalse; + } + MsgPrint (MSG_NORMAL, "succeeded\n"); + + // Switch to lower privileges + MsgPrint (MSG_NORMAL, " - switching to user \"%s\" privileges... ", + low_priv_user); + if (setgid (pw->pw_gid) || setuid (pw->pw_uid)) + { + MsgPrint (MSG_ERROR, "FAILED (%s)\n", strerror (errno)); + return qfalse; + } + MsgPrint (MSG_NORMAL, "succeeded (UID: %u, GID: %u)\n", + pw->pw_uid, pw->pw_gid); + + MsgPrint (MSG_NORMAL, "\n"); + } +#endif + + return qtrue; +} + + +/* +==================== +ParseCommandLine + +Parse the options passed by the command line +==================== +*/ +static qboolean ParseCommandLine (int argc, const char* argv []) +{ + int ind = 1; + unsigned int vlevel = max_msg_level; + qboolean valid_options = qtrue; + + while (ind < argc && valid_options) + { + // If it doesn't even look like an option, why bother? + if (argv[ind][0] != '-') + valid_options = qfalse; + + else switch (argv[ind][1]) + { +#ifndef WIN32 + // Daemon mode + case 'D': + daemon_mode = qtrue; + break; +#endif + + // Help + case 'h': + valid_options = qfalse; + break; + + // Hash size + case 'H': + ind++; + if (ind < argc) + valid_options = Sv_SetHashSize (atoi (argv[ind])); + else + valid_options = qfalse; + break; + +#ifndef WIN32 + // Jail path + case 'j': + ind++; + if (ind < argc) + jail_path = argv[ind]; + else + valid_options = qfalse; + break; +#endif + + // Listen address + case 'l': + ind++; + if (ind >= argc || argv[ind][0] == '\0') + valid_options = qfalse; + else + listen_name = argv[ind]; + break; + + // Address mapping + case 'm': + ind++; + if (ind < argc) + valid_options = Sv_AddAddressMapping (argv[ind]); + else + valid_options = qfalse; + break; + + // Maximum number of servers + case 'n': + ind++; + if (ind < argc) + valid_options = Sv_SetMaxNbServers (atoi (argv[ind])); + else + valid_options = qfalse; + break; + + // Port number + case 'p': + { + unsigned short port_num = 0; + ind++; + if (ind < argc) + port_num = atoi (argv[ind]); + if (!port_num) + valid_options = qfalse; + else + master_port = port_num; + break; + } + +#ifndef WIN32 + // Low privileges user + case 'u': + ind++; + if (ind < argc) + low_priv_user = argv[ind]; + else + valid_options = qfalse; + break; +#endif + + // Verbose level + case 'v': + // If a verbose level has been specified + if (ind + 1 < argc && argv[ind + 1][0] != '-') + { + ind++; + vlevel = atoi (argv[ind]); + if (vlevel > MSG_DEBUG) + valid_options = qfalse; + } + else + vlevel = MSG_DEBUG; + break; + + default: + valid_options = qfalse; + } + + ind++; + } + + // If the command line is OK, we can set the verbose level now + if (valid_options) + { +#ifndef WIN32 + // If we run as a daemon, don't bother printing anything + if (daemon_mode) + max_msg_level = MSG_NOPRINT; + else +#endif + max_msg_level = vlevel; + } + + return valid_options; +} + + +/* +==================== +PrintHelp + +Print the command line syntax and the available options +==================== +*/ +static void PrintHelp (void) +{ + MsgPrint (MSG_ERROR, + "Syntax: dpmaster [options]\n" + "Available options are:\n" +#ifndef WIN32 + " -D : run as a daemon\n" +#endif + " -h : this help\n" + " -H : hash size in bits, up to %u (default: %u)\n" +#ifndef WIN32 + " -j : use as chroot path (default: %s)\n" + " only available when running with super-user privileges\n" +#endif + " -l
: listen on local address
\n" + " -m = : map address to when sending it to clients\n" + " addresses can contain a port number (ex: myaddr.net:1234)\n" + " -n : maximum number of servers recorded (default: %u)\n" + " -p : use port (default: %u)\n" +#ifndef WIN32 + " -u : use privileges (default: %s)\n" + " only available when running with super-user privileges\n" +#endif + " -v [verbose_lvl] : verbose level, up to %u (default: %u; no value means max)\n" + "\n", + MAX_HASH_SIZE, DEFAULT_HASH_SIZE, +#ifndef WIN32 + DEFAULT_JAIL_PATH, +#endif + DEFAULT_MAX_NB_SERVERS, + DEFAULT_MASTER_PORT, +#ifndef WIN32 + DEFAULT_LOW_PRIV_USER, +#endif + MSG_DEBUG, MSG_NORMAL); +} + + +/* +==================== +SecureInit + +System independent initializations, called AFTER the security initializations +==================== +*/ +static qboolean SecureInit (void) +{ + struct sockaddr_in address; + + // Init the time and the random seed + crt_time = time (NULL); + srand (crt_time); + + // Initialize the server list and hash table + if (!Sv_Init ()) + return qfalse; + + // Open the socket + sock = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) + { + MsgPrint (MSG_ERROR, "ERROR: socket creation failed (%s)\n", + strerror (errno)); + return qfalse; + } + + // Bind it to the master port + memset (&address, 0, sizeof (address)); + address.sin_family = AF_INET; + if (listen_name != NULL) + { + MsgPrint (MSG_NORMAL, "Listening on address %s (%s)\n", + listen_name, inet_ntoa (listen_addr)); + address.sin_addr.s_addr = listen_addr.s_addr; + } + else + address.sin_addr.s_addr = htonl (INADDR_ANY); + address.sin_port = htons (master_port); + if (bind (sock, (struct sockaddr*)&address, sizeof (address)) != 0) + { + MsgPrint (MSG_ERROR, "ERROR: socket binding failed (%s)\n", + strerror (errno)); +#ifdef WIN32 + closesocket (sock); +#else + close (sock); +#endif + return qfalse; + } + MsgPrint (MSG_NORMAL, "Listening on UDP port %hu\n", + ntohs (address.sin_port)); + + return qtrue; +} + +static qboolean exitNow = qfalse; + +/* +=============== +cleanUp + +Clean up +=============== +*/ +static void cleanUp( int signal ) +{ + MsgPrint( MSG_NORMAL, "Caught signal %d, exiting...\n", signal ); + + exitNow = qtrue; +} + +/* +==================== +main + +Main function +==================== +*/ +int main (int argc, const char* argv []) +{ + struct sockaddr_in address; + socklen_t addrlen; + int nb_bytes; + char packet [MAX_PACKET_SIZE + 1]; // "+ 1" because we append a '\0' + qboolean valid_options; + struct pollfd socket_poll; + + signal( SIGINT, cleanUp ); + signal( SIGTERM, cleanUp ); + + // Get the options from the command line + valid_options = ParseCommandLine (argc, argv); + + MsgPrint (MSG_NORMAL, + "tremmaster (version " VERSION " " __DATE__ " " __TIME__ ")\n" ); + + // If there was a mistake in the command line, print the help and exit + if (!valid_options) + { + PrintHelp (); + return EXIT_FAILURE; + } + + // Initializations + if (!SysInit () || !UnsecureInit () || !SecInit () || !SecureInit ()) + return EXIT_FAILURE; + MsgPrint (MSG_NORMAL, "\n"); + + memset( &socket_poll, 0, sizeof( struct pollfd ) ); + socket_poll.fd = sock; + socket_poll.events = POLLIN; + + // Until the end of times... + while( !exitNow ) + { + // Check for new data every 100ms + if( poll( &socket_poll, 1, 100 ) <= 0 ) + continue; + + // Get the next valid message + addrlen = sizeof (address); + nb_bytes = recvfrom (sock, packet, sizeof (packet) - 1, 0, + (struct sockaddr*)&address, &addrlen); + if (nb_bytes <= 0) + { + MsgPrint (MSG_WARNING, + "WARNING: \"recvfrom\" returned %d\n", nb_bytes); + continue; + } + + // If we may have to print something, rebuild the peer address buffer + if (max_msg_level != MSG_NOPRINT) + snprintf (peer_address, sizeof (peer_address), "%s:%hu", + inet_ntoa (address.sin_addr), ntohs (address.sin_port)); + + // We print the packet contents if necessary + // TODO: print the current time here + if (max_msg_level >= MSG_DEBUG) + { + MsgPrint (MSG_DEBUG, "New packet received from %s: ", + peer_address); + PrintPacket (packet, nb_bytes); + } + + // A few sanity checks + if (nb_bytes < MIN_PACKET_SIZE) + { + MsgPrint (MSG_WARNING, + "WARNING: rejected packet from %s (size = %d bytes)\n", + peer_address, nb_bytes); + continue; + } + if (*((unsigned int*)packet) != 0xFFFFFFFF) + { + MsgPrint (MSG_WARNING, + "WARNING: rejected packet from %s (invalid header)\n", + peer_address); + continue; + } + if (! ntohs (address.sin_port)) + { + MsgPrint (MSG_WARNING, + "WARNING: rejected packet from %s (source port = 0)\n", + peer_address); + continue; + } + + // Append a '\0' to make the parsing easier and update the current time + packet[nb_bytes] = '\0'; + crt_time = time (NULL); + + // Call HandleMessage with the remaining contents + HandleMessage (packet + 4, nb_bytes - 4, &address); + } + + return 0; +} + + +// ---------- Public functions ---------- // + +/* +==================== +MsgPrint + +Print a message to screen, depending on its verbose level +==================== +*/ +int MsgPrint (msg_level_t msg_level, const char* format, ...) +{ + va_list args; + int result; + + // If the message level is above the maximum level, don't print it + if (msg_level > max_msg_level) + return 0; + + va_start (args, format); + result = vprintf (format, args); + va_end (args); + + fflush (stdout); + + return result; +} diff --git a/src/master/messages.c b/src/master/messages.c new file mode 100644 index 00000000..665b9382 --- /dev/null +++ b/src/master/messages.c @@ -0,0 +1,526 @@ +/* + messages.c + + Message management for tremmaster + + Copyright (C) 2004 Mathieu Olivier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "common.h" +#include "messages.h" +#include "servers.h" + + +// ---------- Constants ---------- // + +// Timeouts (in secondes) +#define TIMEOUT_HEARTBEAT 2 +#define TIMEOUT_INFORESPONSE (15 * 60) + +// Period of validity for a challenge string (in secondes) +#define TIMEOUT_CHALLENGE 2 + +// Maximum size of a reponse packet +#define MAX_PACKET_SIZE 1400 + + +// Types of messages (with samples): + +// "heartbeat Tremulous\n" +#define S2M_HEARTBEAT "heartbeat" + +// "getinfo A_Challenge" +#define M2S_GETINFO "getinfo" + +// "infoResponse\n\\pure\\1\\..." +#define S2M_INFORESPONSE "infoResponse\x0A" + +// "getservers 67 empty full" +#define C2M_GETSERVERS "getservers " + +// "getserversResponse\\...(6 bytes)...\\...(6 bytes)...\\EOT\0\0\0" +#define M2C_GETSERVERSREPONSE "getserversResponse" + +#define C2M_GETMOTD "getmotd" +#define M2C_MOTD "motd " + + +// ---------- Private functions ---------- // + +/* +==================== +SearchInfostring + +Search an infostring for the value of a key +==================== +*/ +static char* SearchInfostring (const char* infostring, const char* key) +{ + static char value [256]; + char crt_key [256]; + size_t value_ind, key_ind; + char c; + + if (*infostring++ != '\\') + return NULL; + + value_ind = 0; + for (;;) + { + key_ind = 0; + + // Get the key name + for (;;) + { + c = *infostring++; + + if (c == '\0') + return NULL; + if (c == '\\' || key_ind == sizeof (crt_key) - 1) + { + crt_key[key_ind] = '\0'; + break; + } + + crt_key[key_ind++] = c; + } + + // If it's the key we are looking for, save it in "value" + if (!strcmp (crt_key, key)) + { + for (;;) + { + c = *infostring++; + + if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1) + { + value[value_ind] = '\0'; + return value; + } + + value[value_ind++] = c; + } + } + + // Else, skip the value + for (;;) + { + c = *infostring++; + + if (c == '\0') + return NULL; + if (c == '\\') + break; + } + } +} + + +/* +==================== +BuildChallenge + +Build a challenge string for a "getinfo" message +==================== +*/ +static const char* BuildChallenge (void) +{ + static char challenge [CHALLENGE_MAX_LENGTH]; + size_t ind; + size_t length = CHALLENGE_MIN_LENGTH - 1; // We start at the minimum size + + // ... then we add a random number of characters + length += rand () % (CHALLENGE_MAX_LENGTH - CHALLENGE_MIN_LENGTH + 1); + + for (ind = 0; ind < length; ind++) + { + char c; + do + { + c = 33 + rand () % (126 - 33 + 1); // -> c = 33..126 + } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/'); + + challenge[ind] = c; + } + + challenge[length] = '\0'; + return challenge; +} + + +/* +==================== +SendGetInfo + +Send a "getinfo" message to a server +==================== +*/ +static void SendGetInfo (server_t* server) +{ + char msg [64] = "\xFF\xFF\xFF\xFF" M2S_GETINFO " "; + + if (!server->challenge_timeout || server->challenge_timeout < crt_time) + { + strncpy (server->challenge, BuildChallenge (), + sizeof (server->challenge) - 1); + server->challenge_timeout = crt_time + TIMEOUT_CHALLENGE; + } + + strncat (msg, server->challenge, sizeof (msg) - strlen (msg) - 1); + sendto (sock, msg, strlen (msg), 0, + (const struct sockaddr*)&server->address, + sizeof (server->address)); + + MsgPrint (MSG_DEBUG, "%s <--- getinfo with challenge \"%s\"\n", + peer_address, server->challenge); +} + + +/* +==================== +HandleGetServers + +Parse getservers requests and send the appropriate response +==================== +*/ +static void HandleGetServers (const char* msg, const struct sockaddr_in* addr) +{ + const char* packetheader = "\xFF\xFF\xFF\xFF" M2C_GETSERVERSREPONSE "\\"; + const size_t headersize = strlen (packetheader); + char packet [MAX_PACKET_SIZE]; + size_t packetind; + server_t* sv; + unsigned int protocol; + unsigned int sv_addr; + unsigned short sv_port; + qboolean no_empty; + qboolean no_full; + unsigned int numServers = 0; + + // Check if there's a name before the protocol number + // In this case, the message comes from a DarkPlaces-compatible client + protocol = atoi (msg); + + MsgPrint (MSG_NORMAL, "%s ---> getservers( protocol version %d )\n", + peer_address, protocol ); + + no_empty = (strstr (msg, "empty") == NULL); + no_full = (strstr (msg, "full") == NULL); + + // Initialize the packet contents with the header + packetind = headersize; + memcpy(packet, packetheader, headersize); + + // Add every relevant server + for (sv = Sv_GetFirst (); /* see below */; sv = Sv_GetNext ()) + { + // If we're done, or if the packet is full, send the packet + if (sv == NULL || packetind > sizeof (packet) - (7 + 6)) + { + // End Of Transmission + packet[packetind ] = 'E'; + packet[packetind + 1] = 'O'; + packet[packetind + 2] = 'T'; + packet[packetind + 3] = '\0'; + packet[packetind + 4] = '\0'; + packet[packetind + 5] = '\0'; + packetind += 6; + + // Send the packet to the client + sendto (sock, packet, packetind, 0, (const struct sockaddr*)addr, + sizeof (*addr)); + + MsgPrint (MSG_DEBUG, "%s <--- getserversResponse (%u servers)\n", + peer_address, numServers); + + // If we're done + if (sv == NULL) + return; + + // Reset the packet index (no need to change the header) + packetind = headersize; + } + + sv_addr = ntohl (sv->address.sin_addr.s_addr); + sv_port = ntohs (sv->address.sin_port); + + // Extra debugging info + if (max_msg_level >= MSG_DEBUG) + { + MsgPrint (MSG_DEBUG, + "Comparing server: IP:\"%u.%u.%u.%u:%hu\", p:%u, c:%hu\n", + sv_addr >> 24, (sv_addr >> 16) & 0xFF, + (sv_addr >> 8) & 0xFF, sv_addr & 0xFF, + sv_port, sv->protocol, sv->nbclients ); + + if (sv->protocol != protocol) + MsgPrint (MSG_DEBUG, + "Reject: protocol %u != requested %u\n", + sv->protocol, protocol); + if (sv->nbclients == 0 && no_empty) + MsgPrint (MSG_DEBUG, + "Reject: nbclients is %hu/%hu && no_empty\n", + sv->nbclients, sv->maxclients); + if (sv->nbclients == sv->maxclients && no_full) + MsgPrint (MSG_DEBUG, + "Reject: nbclients is %hu/%hu && no_full\n", + sv->nbclients, sv->maxclients); + } + + // Check protocol, options + if (sv->protocol != protocol || + (sv->nbclients == 0 && no_empty) || + (sv->nbclients == sv->maxclients && no_full)) + { + + // Skip it + continue; + } + + // Use the address mapping associated with the server, if any + if (sv->addrmap != NULL) + { + const addrmap_t* addrmap = sv->addrmap; + + sv_addr = ntohl (addrmap->to.sin_addr.s_addr); + if (addrmap->to.sin_port != 0) + sv_port = ntohs (addrmap->to.sin_port); + + MsgPrint (MSG_DEBUG, + "Server address mapped to %u.%u.%u.%u:%hu\n", + sv_addr >> 24, (sv_addr >> 16) & 0xFF, + (sv_addr >> 8) & 0xFF, sv_addr & 0xFF, + sv_port); + } + + // IP address + packet[packetind ] = sv_addr >> 24; + packet[packetind + 1] = (sv_addr >> 16) & 0xFF; + packet[packetind + 2] = (sv_addr >> 8) & 0xFF; + packet[packetind + 3] = sv_addr & 0xFF; + + // Port + packet[packetind + 4] = sv_port >> 8; + packet[packetind + 5] = sv_port & 0xFF; + + // Trailing '\' + packet[packetind + 6] = '\\'; + + MsgPrint (MSG_DEBUG, " - Sending server %u.%u.%u.%u:%hu\n", + (qbyte)packet[packetind ], (qbyte)packet[packetind + 1], + (qbyte)packet[packetind + 2], (qbyte)packet[packetind + 3], + sv_port); + + packetind += 7; + numServers++; + } +} + + +/* +==================== +HandleInfoResponse + +Parse infoResponse messages +==================== +*/ +static void HandleInfoResponse (server_t* server, const char* msg) +{ + char* value; + unsigned int new_protocol = 0, new_maxclients = 0; + + MsgPrint (MSG_DEBUG, "%s ---> infoResponse\n", peer_address); + + // Check the challenge + if (!server->challenge_timeout || server->challenge_timeout < crt_time) + { + MsgPrint (MSG_WARNING, + "WARNING: infoResponse with obsolete challenge from %s\n", + peer_address); + return; + } + value = SearchInfostring (msg, "challenge"); + if (!value || strcmp (value, server->challenge)) + { + MsgPrint (MSG_ERROR, "ERROR: invalid challenge from %s (%s)\n", + peer_address, value); + return; + } + + // Check and save the values of "protocol" and "maxclients" + value = SearchInfostring (msg, "protocol"); + if (value) + new_protocol = atoi (value); + value = SearchInfostring (msg, "sv_maxclients"); + if (value) + new_maxclients = atoi (value); + if (!new_protocol || !new_maxclients) + { + MsgPrint (MSG_ERROR, + "ERROR: invalid infoResponse from %s (protocol: %d, maxclients: %d)\n", + peer_address, new_protocol, new_maxclients); + return; + } + server->protocol = new_protocol; + server->maxclients = new_maxclients; + + // Save some other useful values + value = SearchInfostring (msg, "clients"); + if (value) + server->nbclients = atoi (value); + + // Set a new timeout + server->timeout = crt_time + TIMEOUT_INFORESPONSE; +} + + +#define CHALLENGE_KEY "challenge\\" +#define MOTD_KEY "motd\\" + +/* +==================== +HandleGetMotd + +Parse getservers requests and send the appropriate response +==================== +*/ +static void HandleGetMotd( const char* msg, const struct sockaddr_in* addr ) +{ + const char *packetheader = "\xFF\xFF\xFF\xFF" M2C_MOTD "\""; + const size_t headersize = strlen (packetheader); + char packet[ MAX_PACKET_SIZE ]; + char challenge[ MAX_PACKET_SIZE ]; + const char *motd = ""; //FIXME + size_t packetind; + char *value; + + MsgPrint( MSG_DEBUG, "%s ---> getmotd\n", peer_address ); + + value = SearchInfostring( msg, "challenge" ); + if( !value ) + { + MsgPrint( MSG_ERROR, "ERROR: invalid challenge from %s (%s)\n", + peer_address, value ); + return; + } + + strncpy( challenge, value, MAX_PACKET_SIZE ); + + value = SearchInfostring( msg, "renderer" ); + if( value ) + { + //FIXME: create renderer stats + MsgPrint( MSG_DEBUG, "%s is using renderer %s\n", peer_address, value ); + } + + value = SearchInfostring( msg, "version" ); + if( value ) + { + //FIXME: create version stats + MsgPrint( MSG_DEBUG, "%s is using version %s\n", peer_address, value ); + } + + // Initialize the packet contents with the header + packetind = headersize; + memcpy( packet, packetheader, headersize ); + + strncpy( &packet[ packetind ], CHALLENGE_KEY, MAX_PACKET_SIZE - packetind ); + packetind += strlen( CHALLENGE_KEY ); + + strncpy( &packet[ packetind ], challenge, MAX_PACKET_SIZE - packetind ); + packetind += strlen( challenge ); + packet[ packetind++ ] = '\\'; + + strncpy( &packet[ packetind ], MOTD_KEY, MAX_PACKET_SIZE - packetind ); + packetind += strlen( MOTD_KEY ); + + strncpy( &packet[ packetind ], motd, MAX_PACKET_SIZE - packetind ); + packetind += strlen( motd ); + packet[ packetind++ ] = '\"'; + packet[ packetind++ ] = '\0'; + + MsgPrint( MSG_DEBUG, "%s <--- motd\n", peer_address ); + + // Send the packet to the client + sendto( sock, packet, packetind, 0, (const struct sockaddr*)addr, + sizeof( *addr ) ); +} + +// ---------- Public functions ---------- // + +/* +==================== +HandleMessage + +Parse a packet to figure out what to do with it +==================== +*/ +void HandleMessage (const char* msg, size_t length, + const struct sockaddr_in* address) +{ + server_t* server; + + // If it's an heartbeat + if (!strncmp (S2M_HEARTBEAT, msg, strlen (S2M_HEARTBEAT))) + { + char gameId [64]; + + // Extract the game id + sscanf (msg + strlen (S2M_HEARTBEAT) + 1, "%63s", gameId); + MsgPrint (MSG_DEBUG, "%s ---> heartbeat (%s)\n", + peer_address, gameId); + + // Get the server in the list (add it to the list if necessary) + server = Sv_GetByAddr (address, qtrue); + if (server == NULL) + return; + + server->active = qtrue; + + // If we haven't yet received any infoResponse from this server, + // we let it some more time to contact us. After that, only + // infoResponse messages can update the timeout value. + if (!server->maxclients) + server->timeout = crt_time + TIMEOUT_HEARTBEAT; + + // Ask for some infos + SendGetInfo (server); + } + + // If it's an infoResponse message + else if (!strncmp (S2M_INFORESPONSE, msg, strlen (S2M_INFORESPONSE))) + { + server = Sv_GetByAddr (address, qfalse); + if (server == NULL) + return; + + HandleInfoResponse (server, msg + strlen (S2M_INFORESPONSE)); + } + + // If it's a getservers request + else if (!strncmp (C2M_GETSERVERS, msg, strlen (C2M_GETSERVERS))) + { + HandleGetServers (msg + strlen (C2M_GETSERVERS), address); + } + + // If it's a getmotd request + else if (!strncmp (C2M_GETMOTD, msg, strlen (C2M_GETMOTD))) + { + HandleGetMotd (msg + strlen (C2M_GETMOTD), address); + } +} diff --git a/src/master/messages.h b/src/master/messages.h new file mode 100644 index 00000000..93b52716 --- /dev/null +++ b/src/master/messages.h @@ -0,0 +1,35 @@ +/* + messages.h + + Message management for dpmaster + + Copyright (C) 2004 Mathieu Olivier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef _MESSAGES_H_ +#define _MESSAGES_H_ + + +// ---------- Public functions ---------- // + +// Parse a packet to figure out what to do with it +void HandleMessage (const char* msg, size_t length, + const struct sockaddr_in* address); + + +#endif // #ifndef _MESSAGES_H_ diff --git a/src/master/servers.c b/src/master/servers.c new file mode 100644 index 00000000..a8412a1d --- /dev/null +++ b/src/master/servers.c @@ -0,0 +1,666 @@ +/* + servers.c + + Server list and address mapping management for dpmaster + + Copyright (C) 2004 Mathieu Olivier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "common.h" +#include "servers.h" + + +// ---------- Constants ---------- // + +// Address hash bitmask +#define HASH_BITMASK (hash_table_size - 1) + + +// ---------- Variables ---------- // + +// All server structures are allocated in one block in the "servers" array. +// Each used slot is also part of a linked list in "hash_table". A simple +// hash of the address and port of a server gives its index in the table. +static server_t* servers = NULL; +static size_t max_nb_servers = DEFAULT_MAX_NB_SERVERS; +static size_t nb_servers = 0; +static server_t** hash_table = NULL; +static size_t hash_table_size = (1 << DEFAULT_HASH_SIZE); + +// Last allocated entry in the "servers" array. +// Used as a start index for finding a free slot in "servers" more quickly +static unsigned int last_alloc; + +// Variables for Sv_GetFirst, Sv_GetNext and Sv_RemoveCurrentAndGetNext +static server_t* crt_server = NULL; +static server_t** prev_pointer = NULL; +static int crt_hash_ind = -1; + +// List of address mappings. They are sorted by "from" field (IP, then port) +static addrmap_t* addrmaps = NULL; + + +// ---------- Private functions ---------- // + +/* +==================== +Sv_AddressHash + +Compute the hash of a server address +==================== +*/ +static unsigned int Sv_AddressHash (const struct sockaddr_in* address) +{ + qbyte* addr = (qbyte*)&address->sin_addr.s_addr; + qbyte* port = (qbyte*)&address->sin_port; + qbyte hash; + + hash = addr[0] ^ addr[1] ^ addr[2] ^ addr[3] ^ port[0] ^ port[1]; + return hash & HASH_BITMASK; +} + + +/* +==================== +Sv_RemoveAndGetNextPtr + +Remove a server from the list and returns its "next" pointer +==================== +*/ +static server_t* Sv_RemoveAndGetNextPtr (server_t* sv, server_t** prev) +{ + nb_servers--; + MsgPrint (MSG_NORMAL, + "%s:%hu timed out; %u servers currently registered\n", + inet_ntoa (sv->address.sin_addr), ntohs (sv->address.sin_port), + nb_servers); + + // Mark this structure as "free" + sv->active = qfalse; + + *prev = sv->next; + return sv->next; +} + + +/* +==================== +Sv_ResolveAddr + +Resolve an internet address +name may include a port number, after a ':' +==================== +*/ +static qboolean Sv_ResolveAddr (const char* name, struct sockaddr_in* addr) +{ + char *namecpy, *port; + struct hostent* host; + + // Create a work copy + namecpy = strdup (name); + if (namecpy == NULL) + { + MsgPrint (MSG_ERROR, + "ERROR: can't allocate enough memory to resolve %s\n", + name); + return qfalse; + } + + // Find the port in the address + port = strchr (namecpy, ':'); + if (port != NULL) + *port++ = '\0'; + + // Resolve the address + host = gethostbyname (namecpy); + if (host == NULL) + { + MsgPrint (MSG_ERROR, "ERROR: can't resolve %s\n", namecpy); + free (namecpy); + return qfalse; + } + if (host->h_addrtype != AF_INET) + { + MsgPrint (MSG_ERROR, "ERROR: %s is not an IPv4 address\n", + namecpy); + free (namecpy); + return qfalse; + } + + // Build the structure + memset (addr, 0, sizeof (*addr)); + addr->sin_family = AF_INET; + memcpy (&addr->sin_addr.s_addr, host->h_addr, + sizeof (addr->sin_addr.s_addr)); + if (port != NULL) + addr->sin_port = htons ((unsigned short)atoi (port)); + + MsgPrint (MSG_DEBUG, "\"%s\" resolved to %s:%hu\n", + name, inet_ntoa (addr->sin_addr), ntohs (addr->sin_port)); + + free (namecpy); + return qtrue; +} + + +/* +==================== +Sv_InsertAddrmapIntoList + +Insert an addrmap structure to the addrmaps list +==================== +*/ +static void Sv_InsertAddrmapIntoList (addrmap_t* new_map) +{ + addrmap_t* addrmap = addrmaps; + addrmap_t** prev = &addrmaps; + + // Stop at the end of the list, or if the addresses become too high + while (addrmap != NULL && + addrmap->from.sin_addr.s_addr <= new_map->from.sin_addr.s_addr) + { + // If we found the right place + if (addrmap->from.sin_addr.s_addr == new_map->from.sin_addr.s_addr && + addrmap->from.sin_port >= new_map->from.sin_port) + { + // If a mapping is already recorded for this address + if (addrmap->from.sin_port == new_map->from.sin_port) + { + MsgPrint (MSG_WARNING, + "WARNING: Address %s:%hu has several mappings\n", + inet_ntoa (new_map->from.sin_addr), + ntohs (new_map->from.sin_port)); + + *prev = addrmap->next; + free (addrmap); + } + break; + } + + prev = &addrmap->next; + addrmap = addrmap->next; + } + + // Insert it + new_map->next = *prev; + *prev = new_map; + + MsgPrint (MSG_NORMAL, "Address \"%s\" mapped to \"%s\" (%s:%hu)\n", + new_map->from_string, new_map->to_string, + inet_ntoa (new_map->to.sin_addr), ntohs (new_map->to.sin_port)); +} + + +/* +==================== +Sv_GetAddrmap + +Look for an address mapping corresponding to addr +==================== +*/ +static const addrmap_t* Sv_GetAddrmap (const struct sockaddr_in* addr) +{ + const addrmap_t* addrmap = addrmaps; + const addrmap_t* found = NULL; + + // Stop at the end of the list, or if the addresses become too high + while (addrmap != NULL && + addrmap->from.sin_addr.s_addr <= addr->sin_addr.s_addr) + { + // If it's the right address + if (addrmap->from.sin_addr.s_addr == addr->sin_addr.s_addr) + { + // If the exact mapping isn't there + if (addrmap->from.sin_port > addr->sin_port) + return found; + + // If we found the exact address + if (addrmap->from.sin_port == addr->sin_port) + return addrmap; + + // General mapping + // Store it in case we don't find the exact address mapping + if (addrmap->from.sin_port == 0) + found = addrmap; + } + + addrmap = addrmap->next; + } + + return found; +} + + +/* +==================== +Sv_ResolveAddrmap + +Resolve an addrmap structure and check the parameters validity +==================== +*/ +static qboolean Sv_ResolveAddrmap (addrmap_t* addrmap) +{ + // Resolve the addresses + if (!Sv_ResolveAddr (addrmap->from_string, &addrmap->from) || + !Sv_ResolveAddr (addrmap->to_string, &addrmap->to)) + return qfalse; + + // 0.0.0.0 addresses are forbidden + if (addrmap->from.sin_addr.s_addr == 0 || + addrmap->to.sin_addr.s_addr == 0) + { + MsgPrint (MSG_ERROR, + "ERROR: Mapping from or to 0.0.0.0 is forbidden\n"); + return qfalse; + } + + // Do NOT allow mapping to loopback addresses + if ((ntohl (addrmap->to.sin_addr.s_addr) >> 24) == 127) + { + MsgPrint (MSG_ERROR, + "ERROR: Mapping to a loopback address is forbidden\n"); + return qfalse; + } + + return qtrue; +} + + +/* +==================== +Sv_IsActive + +Return qtrue if a server is active. +Test if the server has timed out and remove it if it's the case. +==================== +*/ +static qboolean Sv_IsActive (server_t* server) +{ + + // If the entry isn't even used + if (! server->active) + return qfalse; + + // If the server has timed out + if (server->timeout < crt_time) + { + unsigned int hash; + server_t **prev, *sv; + + hash = Sv_AddressHash (&server->address); + prev = &hash_table[hash]; + sv = hash_table[hash]; + + while (sv != server) + { + prev = &sv->next; + sv = sv->next; + } + + Sv_RemoveAndGetNextPtr (sv, prev); + return qfalse; + } + + return qtrue; +} + + +// ---------- Public functions (servers) ---------- // + +/* +==================== +Sv_SetHashSize + +Set a new hash size value +==================== +*/ +qboolean Sv_SetHashSize (unsigned int size) +{ + // Too late? + if (hash_table != NULL) + return qfalse; + + // Too big? + if (size > MAX_HASH_SIZE) + return qfalse; + + hash_table_size = 1 << size; + return qtrue; +} + + +/* +==================== +Sv_SetMaxNbServers + +Set a new hash size value +==================== +*/ +qboolean Sv_SetMaxNbServers (unsigned int nb) +{ + // Too late? + if (servers != NULL) + return qfalse; + + max_nb_servers = nb; + return qtrue; +} + + +/* +==================== +Sv_Init + +Initialize the server list and hash table +==================== +*/ +qboolean Sv_Init (void) +{ + size_t array_size; + + // Allocate "servers" and clean it + array_size = max_nb_servers * sizeof (servers[0]); + servers = malloc (array_size); + if (!servers) + { + MsgPrint (MSG_ERROR, + "ERROR: can't allocate the servers array (%s)\n", + strerror (errno)); + return qfalse; + } + last_alloc = max_nb_servers - 1; + memset (servers, 0, array_size); + MsgPrint (MSG_NORMAL, "%u server records allocated\n", max_nb_servers); + + // Allocate "hash_table" and clean it + array_size = hash_table_size * sizeof (hash_table[0]); + hash_table = malloc (array_size); + if (!hash_table) + { + MsgPrint (MSG_ERROR, "ERROR: can't allocate the hash table (%s)\n", + strerror (errno)); + free (servers); + return qfalse; + } + memset (hash_table, 0, array_size); + MsgPrint (MSG_NORMAL, + "Hash table allocated (%u entries)\n", hash_table_size); + + return qtrue; +} + + +/* +==================== +Sv_GetByAddr + +Search for a particular server in the list; add it if necessary +==================== +*/ +server_t* Sv_GetByAddr (const struct sockaddr_in* address, qboolean add_it) +{ + server_t **prev, *sv; + unsigned int hash; + const addrmap_t* addrmap = Sv_GetAddrmap (address); + unsigned int startpt; + + // Allow servers on a loopback address ONLY if a mapping is defined for them + if ((ntohl (address->sin_addr.s_addr) >> 24) == 127 && addrmap == NULL) + { + MsgPrint (MSG_WARNING, + "WARNING: server %s isn't allowed (loopback address)\n", + peer_address); + return NULL; + } + + hash = Sv_AddressHash (address); + prev = &hash_table[hash]; + sv = hash_table[hash]; + + while (sv != NULL) + { + // We check the timeout values while browsing this list + if (sv->timeout < crt_time) + { + sv = Sv_RemoveAndGetNextPtr (sv, prev); + continue; + } + + // Found! + if (sv->address.sin_addr.s_addr == address->sin_addr.s_addr && + sv->address.sin_port == address->sin_port) + { + // Put it on top of the list (it's useful because heartbeats + // are almost always followed by infoResponses) + *prev = sv->next; + sv->next = hash_table[hash]; + hash_table[hash] = sv; + + return sv; + } + + prev = &sv->next; + sv = sv->next; + } + + if (! add_it) + return NULL; + + // Look for the first free entry in "servers" + startpt = last_alloc; + for (;;) + { + last_alloc = (last_alloc + 1) % max_nb_servers; + + // Free entry found? + if (!Sv_IsActive (&servers[last_alloc])) + break; + + // No more room + if (last_alloc == startpt) + return NULL; + } + sv = &servers[last_alloc]; + + // Initialize the structure + memset (sv, 0, sizeof (*sv)); + memcpy (&sv->address, address, sizeof (sv->address)); + sv->addrmap = addrmap; + + // Add it to the list it belongs to + sv->next = hash_table[hash]; + hash_table[hash] = sv; + nb_servers++; + + MsgPrint (MSG_NORMAL, + "New server added: %s; %u servers are currently registered\n", + peer_address, nb_servers); + MsgPrint (MSG_DEBUG, + " - index: %u\n" + " - hash: 0x%02X\n", + last_alloc, hash); + + return sv; +} + + +/* +==================== +Sv_GetFirst + +Get the first server in the list +==================== +*/ +server_t* Sv_GetFirst (void) +{ + crt_server = NULL; + prev_pointer = NULL; + crt_hash_ind = -1; + + return Sv_GetNext (); +} + + +/* +==================== +Sv_GetNext + +Get the next server in the list +==================== +*/ +server_t* Sv_GetNext (void) +{ + for (;;) + { + // If there is a current server, follow the link + if (crt_server != NULL) + { + prev_pointer = &crt_server->next; + crt_server = crt_server->next; + } + + // If we don't have the next server yet + if (crt_server == NULL) + { + // Search the hash table for the next server + while (crt_hash_ind < (int)(hash_table_size - 1)) + { + crt_hash_ind++; + + if (hash_table[crt_hash_ind] != NULL) + { + crt_server = hash_table[crt_hash_ind]; + prev_pointer = &hash_table[crt_hash_ind]; + break; + } + } + } + + // Did we hit the end of the list? + if (crt_server == NULL) + return NULL; + + // If the new current server has timed out, remove it + if (crt_server->timeout < crt_time) + crt_server = Sv_RemoveAndGetNextPtr (crt_server, prev_pointer); + else + return crt_server; + } +} + + +// ---------- Public functions (address mappings) ---------- // + +/* +==================== +Sv_AddAddressMapping + +Add an unresolved address mapping to the list +mapping must be of the form "addr1:port1=addr2:port2", ":portX" are optional +==================== +*/ +qboolean Sv_AddAddressMapping (const char* mapping) +{ + char *map_string, *to_ip; + addrmap_t* addrmap; + + // Get a working copy of the mapping string + map_string = strdup (mapping); + if (map_string == NULL) + { + MsgPrint (MSG_ERROR, + "ERROR: can't allocate address mapping string\n"); + return qfalse; + } + + // Find the '=' + to_ip = strchr (map_string, '='); + if (to_ip == NULL) + { + MsgPrint (MSG_ERROR, + "ERROR: invalid syntax in address mapping string\n"); + free (map_string); + return qfalse; + } + *to_ip++ = '\0'; + + // Allocate the structure + addrmap = malloc (sizeof (*addrmap)); + if (addrmap == NULL) + { + MsgPrint (MSG_ERROR, + "ERROR: can't allocate address mapping structure\n"); + free (map_string); + return qfalse; + } + memset (addrmap, 0, sizeof (*addrmap)); + addrmap->from_string = strdup (map_string); + addrmap->to_string = strdup (to_ip); + if (addrmap->from_string == NULL || addrmap->to_string == NULL) + { + MsgPrint (MSG_ERROR, + "ERROR: can't allocate address mapping strings\n"); + free (addrmap->to_string); + free (addrmap->from_string); + free (map_string); + return qfalse; + } + + // Add it on top of "addrmaps" + addrmap->next = addrmaps; + addrmaps = addrmap; + + return qtrue; +} + + +/* +==================== +Sv_ResolveAddressMappings + +Resolve the address mapping list +==================== +*/ +qboolean Sv_ResolveAddressMappings (void) +{ + addrmap_t* unresolved = addrmaps; + addrmap_t* addrmap; + qboolean succeeded = qtrue; + + addrmaps = NULL; + + while (unresolved != NULL) + { + // Remove it from the unresolved list + addrmap = unresolved; + unresolved = unresolved->next; + + // Continue the resolution, even if there's an error + if (!Sv_ResolveAddrmap (addrmap)) + { + free (addrmap->from_string); + free (addrmap->to_string); + free (addrmap); + succeeded = qfalse; + } + else + Sv_InsertAddrmapIntoList (addrmap); + } + + return succeeded; +} diff --git a/src/master/servers.h b/src/master/servers.h new file mode 100644 index 00000000..fbeef1c2 --- /dev/null +++ b/src/master/servers.h @@ -0,0 +1,102 @@ +/* + servers.h + + Server list and address mapping management for dpmaster + + Copyright (C) 2004-2005 Mathieu Olivier + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef _SERVERS_H_ +#define _SERVERS_H_ + + +// ---------- Constants ---------- // + +// Maximum number of servers in all lists by default +#define DEFAULT_MAX_NB_SERVERS 256 + +// Address hash size in bits (between 0 and MAX_HASH_SIZE) +#define DEFAULT_HASH_SIZE 6 +#define MAX_HASH_SIZE 8 + +// Number of characters in a challenge, including the '\0' +#define CHALLENGE_MIN_LENGTH 9 +#define CHALLENGE_MAX_LENGTH 12 + +// ---------- Types ---------- // + +// Address mapping +typedef struct addrmap_s +{ + struct addrmap_s* next; + struct sockaddr_in from; + struct sockaddr_in to; + char* from_string; + char* to_string; +} addrmap_t; + +// Server properties +typedef struct server_s +{ + struct server_s* next; + struct sockaddr_in address; + unsigned int protocol; + char challenge [CHALLENGE_MAX_LENGTH]; + unsigned short nbclients; + unsigned short maxclients; + time_t timeout; + time_t challenge_timeout; + const struct addrmap_s* addrmap; + qboolean active; +} server_t; + + +// ---------- Public functions (servers) ---------- // + +// Will simply return "false" if called after Sv_Init +qboolean Sv_SetHashSize (unsigned int size); +qboolean Sv_SetMaxNbServers (unsigned int nb); + +// Initialize the server list and hash table +qboolean Sv_Init (void); + +// Search for a particular server in the list; add it if necessary +// NOTE: doesn't change the current position for "Sv_GetNext" +server_t* Sv_GetByAddr (const struct sockaddr_in* address, qboolean add_it); + +// Get the first server in the list +server_t* Sv_GetFirst (void); + +// Get the next server in the list +server_t* Sv_GetNext (void); + + +// ---------- Public functions (address mappings) ---------- // + +// NOTE: this is a 2-step process because resolving address mappings directly +// during the parsing of the command line would cause several problems + +// Add an unresolved address mapping to the list +// mapping must be of the form "addr1:port1=addr2:port2", ":portX" are optional +qboolean Sv_AddAddressMapping (const char* mapping); + +// Resolve the address mapping list +qboolean Sv_ResolveAddressMappings (void); + + +#endif // #ifndef _SERVERS_H_ diff --git a/src/null/null_client.c b/src/null/null_client.c index 039aa7c2..994f53f4 100644 --- a/src/null/null_client.c +++ b/src/null/null_client.c @@ -87,6 +87,3 @@ void CL_StartHunkUsers( void ) { // bk001119 - added new dummy for sv_init.c void CL_ShutdownAll(void) {}; - -// bk001208 - added new dummy (RC4) -qboolean CL_CDKeyValidate( const char *key, const char *checksum ) { return qtrue; } diff --git a/src/qcommon/cm_trace.c b/src/qcommon/cm_trace.c index 483b0756..ee9540e3 100644 --- a/src/qcommon/cm_trace.c +++ b/src/qcommon/cm_trace.c @@ -738,7 +738,8 @@ static void CM_ProximityToBrush( traceWork_t *tw, cbrush_t *brush ) cbrushedge_t *edge; float dist, minDist = 1e+10f; float s, t; - float sAtMin, radius, fraction; + float sAtMin = 0.0f; + float radius = 0.0f, fraction; traceWork_t tw2; // cheapish purely linear trace to test for intersection diff --git a/src/qcommon/cmd.c b/src/qcommon/cmd.c index 2968f708..a3053c89 100644 --- a/src/qcommon/cmd.c +++ b/src/qcommon/cmd.c @@ -315,12 +315,37 @@ typedef struct cmd_function_s } cmd_function_t; -static int cmd_argc; -static char *cmd_argv[MAX_STRING_TOKENS]; // points into cmd_tokenized -static char cmd_tokenized[BIG_INFO_STRING+MAX_STRING_TOKENS]; // will have 0 bytes inserted -static char cmd_cmd[BIG_INFO_STRING]; // the original command we received (no token processing) +typedef struct cmdContext_s +{ + int argc; + char *argv[ MAX_STRING_TOKENS ]; // points into cmd.tokenized + char tokenized[ BIG_INFO_STRING + MAX_STRING_TOKENS ]; // will have 0 bytes inserted + char cmd[ BIG_INFO_STRING ]; // the original command we received (no token processing) +} cmdContext_t; + +static cmdContext_t cmd; +static cmdContext_t savedCmd; +static cmd_function_t *cmd_functions; // possible commands to execute -static cmd_function_t *cmd_functions; // possible commands to execute +/* +============ +Cmd_SaveCmdContext +============ +*/ +void Cmd_SaveCmdContext( void ) +{ + Com_Memcpy( &savedCmd, &cmd, sizeof( cmdContext_t ) ); +} + +/* +============ +Cmd_RestoreCmdContext +============ +*/ +void Cmd_RestoreCmdContext( void ) +{ + Com_Memcpy( &cmd, &savedCmd, sizeof( cmdContext_t ) ); +} /* ============ @@ -328,7 +353,7 @@ Cmd_Argc ============ */ int Cmd_Argc( void ) { - return cmd_argc; + return cmd.argc; } /* @@ -337,10 +362,10 @@ Cmd_Argv ============ */ char *Cmd_Argv( int arg ) { - if ( (unsigned)arg >= cmd_argc ) { + if ( (unsigned)arg >= cmd.argc ) { return ""; } - return cmd_argv[arg]; + return cmd.argv[arg]; } /* @@ -368,9 +393,9 @@ char *Cmd_Args( void ) { int i; cmd_args[0] = 0; - for ( i = 1 ; i < cmd_argc ; i++ ) { - strcat( cmd_args, cmd_argv[i] ); - if ( i != cmd_argc-1 ) { + for ( i = 1 ; i < cmd.argc ; i++ ) { + strcat( cmd_args, cmd.argv[i] ); + if ( i != cmd.argc-1 ) { strcat( cmd_args, " " ); } } @@ -392,9 +417,9 @@ char *Cmd_ArgsFrom( int arg ) { cmd_args[0] = 0; if (arg < 0) arg = 0; - for ( i = arg ; i < cmd_argc ; i++ ) { - strcat( cmd_args, cmd_argv[i] ); - if ( i != cmd_argc-1 ) { + for ( i = arg ; i < cmd.argc ; i++ ) { + strcat( cmd_args, cmd.argv[i] ); + if ( i != cmd.argc-1 ) { strcat( cmd_args, " " ); } } @@ -423,7 +448,7 @@ they can't have pointers returned to them ============ */ void Cmd_LiteralArgsBuffer( char *buffer, int bufferLength ) { - Q_strncpyz( buffer, cmd_cmd, bufferLength ); + Q_strncpyz( buffer, cmd.cmd, bufferLength ); } /* @@ -437,7 +462,7 @@ https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543 */ char *Cmd_Cmd(void) { - return cmd_cmd; + return cmd.cmd; } /* @@ -462,20 +487,20 @@ void Cmd_TokenizeString( const char *text_in ) { #endif // clear previous args - cmd_argc = 0; - cmd_cmd[ 0 ] = '\0'; + cmd.argc = 0; + cmd.cmd[ 0 ] = '\0'; if ( !text_in ) { return; } - Q_strncpyz( cmd_cmd, text_in, sizeof(cmd_cmd) ); + Q_strncpyz( cmd.cmd, text_in, sizeof(cmd.cmd) ); text = text_in; - textOut = cmd_tokenized; + textOut = cmd.tokenized; while ( 1 ) { - if ( cmd_argc == MAX_STRING_TOKENS ) { + if ( cmd.argc == MAX_STRING_TOKENS ) { return; // this is usually something malicious } @@ -510,8 +535,8 @@ void Cmd_TokenizeString( const char *text_in ) { // handle quoted strings // NOTE TTimo this doesn't handle \" escaping if ( *text == '"' ) { - cmd_argv[cmd_argc] = textOut; - cmd_argc++; + cmd.argv[cmd.argc] = textOut; + cmd.argc++; text++; while ( *text && *text != '"' ) { *textOut++ = *text++; @@ -525,8 +550,8 @@ void Cmd_TokenizeString( const char *text_in ) { } // regular token - cmd_argv[cmd_argc] = textOut; - cmd_argc++; + cmd.argv[cmd.argc] = textOut; + cmd.argc++; // skip until whitespace, quote, or command while ( *text > ' ' ) { @@ -633,7 +658,7 @@ A complete command line has been parsed, so try to execute it ============ */ void Cmd_ExecuteString( const char *text ) { - cmd_function_t *cmd, **prev; + cmd_function_t *cmdFunc, **prev; // execute the command line Cmd_TokenizeString( text ); @@ -642,21 +667,21 @@ void Cmd_ExecuteString( const char *text ) { } // check registered command functions - for ( prev = &cmd_functions ; *prev ; prev = &cmd->next ) { - cmd = *prev; - if ( !Q_stricmp( cmd_argv[0],cmd->name ) ) { + for ( prev = &cmd_functions ; *prev ; prev = &cmdFunc->next ) { + cmdFunc = *prev; + if ( !Q_stricmp( cmd.argv[0], cmdFunc->name ) ) { // rearrange the links so that the command will be // near the head of the list next time it is used - *prev = cmd->next; - cmd->next = cmd_functions; - cmd_functions = cmd; + *prev = cmdFunc->next; + cmdFunc->next = cmd_functions; + cmd_functions = cmdFunc; // perform the action - if ( !cmd->function ) { + if ( !cmdFunc->function ) { // let the cgame or game handle it break; } else { - cmd->function (); + cmdFunc->function (); } return; } diff --git a/src/qcommon/common.c b/src/qcommon/common.c index 6ba479e3..231f4aa8 100644 --- a/src/qcommon/common.c +++ b/src/qcommon/common.c @@ -33,7 +33,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #endif int demo_protocols[] = -{ 66, 67, 68, 0 }; +{ 66, 67, 68, 69, 0 }; #define MAX_NUM_ARGVS 50 @@ -2229,76 +2229,6 @@ static void Com_Crash_f( void ) { * ( int * ) 0 = 0x12345678; } -// TTimo: centralizing the cl_cdkey stuff after I discovered a buffer overflow problem with the dedicated server version -// not sure it's necessary to have different defaults for regular and dedicated, but I don't want to risk it -// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=470 -#ifndef DEDICATED -char cl_cdkey[34] = " "; -#else -char cl_cdkey[34] = "123456789"; -#endif - -/* -================= -Com_ReadCDKey -================= -*/ -qboolean CL_CDKeyValidate( const char *key, const char *checksum ); -void Com_ReadCDKey( const char *filename ) { - fileHandle_t f; - char buffer[33]; - char fbuffer[MAX_OSPATH]; - - sprintf(fbuffer, "%s/q3key", filename); - - FS_SV_FOpenFileRead( fbuffer, &f ); - if ( !f ) { - Q_strncpyz( cl_cdkey, " ", 17 ); - return; - } - - Com_Memset( buffer, 0, sizeof(buffer) ); - - FS_Read( buffer, 16, f ); - FS_FCloseFile( f ); - - if (CL_CDKeyValidate(buffer, NULL)) { - Q_strncpyz( cl_cdkey, buffer, 17 ); - } else { - Q_strncpyz( cl_cdkey, " ", 17 ); - } -} - -/* -================= -Com_AppendCDKey -================= -*/ -void Com_AppendCDKey( const char *filename ) { - fileHandle_t f; - char buffer[33]; - char fbuffer[MAX_OSPATH]; - - sprintf(fbuffer, "%s/q3key", filename); - - FS_SV_FOpenFileRead( fbuffer, &f ); - if (!f) { - Q_strncpyz( &cl_cdkey[16], " ", 17 ); - return; - } - - Com_Memset( buffer, 0, sizeof(buffer) ); - - FS_Read( buffer, 16, f ); - FS_FCloseFile( f ); - - if (CL_CDKeyValidate(buffer, NULL)) { - strcat( &cl_cdkey[16], buffer ); - } else { - Q_strncpyz( &cl_cdkey[16], " ", 17 ); - } -} - static void Com_DetectAltivec(void) { // Only detect if user hasn't forcibly disabled it. diff --git a/src/qcommon/cvar.c b/src/qcommon/cvar.c index 6c0f612f..839f41ce 100644 --- a/src/qcommon/cvar.c +++ b/src/qcommon/cvar.c @@ -640,9 +640,6 @@ void Cvar_WriteVariables( fileHandle_t f ) { char buffer[1024]; for (var = cvar_vars ; var ; var = var->next) { - if( Q_stricmp( var->name, "cl_cdkey" ) == 0 ) { - continue; - } if( var->flags & CVAR_ARCHIVE ) { // write the latched value, even if it hasn't taken effect yet if ( var->latchedString ) { diff --git a/src/qcommon/files.c b/src/qcommon/files.c index 09926a1a..1ea34cc6 100644 --- a/src/qcommon/files.c +++ b/src/qcommon/files.c @@ -2702,9 +2702,6 @@ void FS_Shutdown( qboolean closemfp ) { #endif } -void Com_AppendCDKey( const char *filename ); -void Com_ReadCDKey( const char *filename ); - /* ================ FS_ReorderPurePaks @@ -2752,7 +2749,6 @@ FS_Startup */ static void FS_Startup( const char *gameName ) { const char *homePath; - cvar_t *fs; Com_Printf( "----- FS_Startup -----\n" ); @@ -2808,12 +2804,6 @@ static void FS_Startup( const char *gameName ) { } } - Com_ReadCDKey( "base" ); - fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); - if (fs && fs->string[0] != 0) { - Com_AppendCDKey( fs->string ); - } - // add our commands Cmd_AddCommand ("path", FS_Path_f); Cmd_AddCommand ("dir", FS_Dir_f ); diff --git a/src/qcommon/q_shared.h b/src/qcommon/q_shared.h index 6dd8527d..53848aa4 100644 --- a/src/qcommon/q_shared.h +++ b/src/qcommon/q_shared.h @@ -1274,8 +1274,4 @@ typedef enum _flag_status { #define SAY_TEAM 1 #define SAY_TELL 2 -#define CDKEY_LEN 16 -#define CDCHKSUM_LEN 2 - - #endif // __Q_SHARED_H diff --git a/src/qcommon/qcommon.h b/src/qcommon/qcommon.h index 11c332ce..843f04ef 100644 --- a/src/qcommon/qcommon.h +++ b/src/qcommon/qcommon.h @@ -227,27 +227,20 @@ PROTOCOL ============================================================== */ -#define PROTOCOL_VERSION 68 -// 1.31 - 67 +// 69 is identical in every way to 68 - the change is to avoid +// confusing connecting Q3 clients +#define PROTOCOL_VERSION 69 // maintain a list of compatible protocols for demo playing // NOTE: that stuff only works with two digits protocols extern int demo_protocols[]; -#define UPDATE_SERVER_NAME "update.quake3arena.com" // override on command line, config files etc. #ifndef MASTER_SERVER_NAME -#define MASTER_SERVER_NAME "master.quake3arena.com" -#endif -#ifndef AUTHORIZE_SERVER_NAME -#define AUTHORIZE_SERVER_NAME "authorize.quake3arena.com" +#define MASTER_SERVER_NAME "master.tremulous.net" #endif #define PORT_MASTER 27950 -#define PORT_UPDATE 27951 -#ifndef PORT_AUTHORIZE -#define PORT_AUTHORIZE 27952 -#endif #define PORT_SERVER 27960 #define NUM_SERVER_PORTS 4 // broadcast scan this many ports after // PORT_SERVER so a single machine can @@ -424,6 +417,8 @@ void Cmd_ExecuteString( const char *text ); // Parses a single line of text into arguments and tries to execute it // as if it was typed at the console +void Cmd_SaveCmdContext( void ); +void Cmd_RestoreCmdContext( void ); /* ============================================================== @@ -693,10 +688,6 @@ MISC #define Q_vsnprintf vsnprintf #endif -// centralizing the declarations for cl_cdkey -// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=470 -extern char cl_cdkey[34]; - // returnbed by Sys_GetProcessorId #define CPUID_GENERIC 0 // any unrecognized processor @@ -916,7 +907,6 @@ qboolean SV_GameCommand( void ); // UI interface // qboolean UI_GameCommand( void ); -qboolean UI_usesUniqueCDKey(void); /* ============================================================== diff --git a/src/server/server.h b/src/server/server.h index 6740a82d..da8c8aae 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -242,7 +242,6 @@ extern cvar_t *sv_serverid; extern cvar_t *sv_maxRate; extern cvar_t *sv_minPing; extern cvar_t *sv_maxPing; -extern cvar_t *sv_gametype; extern cvar_t *sv_pure; extern cvar_t *sv_floodProtect; extern cvar_t *sv_lanForceRate; diff --git a/src/server/sv_ccmds.c b/src/server/sv_ccmds.c index 9128249f..fde921d1 100644 --- a/src/server/sv_ccmds.c +++ b/src/server/sv_ccmds.c @@ -154,9 +154,6 @@ static void SV_Map_f( void ) { return; } - // force latched values to get set - Cvar_Get ("g_gametype", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH ); - cmd = Cmd_Argv(0); if ( !Q_stricmp( cmd, "devmap" ) || !Q_stricmp( cmd, "spdevmap" ) ) { cheat = qtrue; @@ -228,7 +225,7 @@ static void SV_MapRestart_f( void ) { // check for changes in variables that can't just be restarted // check for maxclients change - if ( sv_maxclients->modified || sv_gametype->modified ) { + if ( sv_maxclients->modified ) { char mapname[MAX_QPATH]; Com_Printf( "variable change -- restarting.\n" ); @@ -400,6 +397,8 @@ static void SV_Ban_f( void ) { return; } + //FIXME: there is no auth server in Tremulous +#if 0 // look up the authorize server's IP if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) { Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); @@ -421,6 +420,7 @@ static void SV_Ban_f( void ) { cl->netchan.remoteAddress.ip[2], cl->netchan.remoteAddress.ip[3] ); Com_Printf("%s was banned from coming back\n", cl->name); } +#endif } /* @@ -454,6 +454,8 @@ static void SV_BanNum_f( void ) { return; } + //FIXME: there is no auth server in Tremulous +#if 0 // look up the authorize server's IP if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) { Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); @@ -475,6 +477,7 @@ static void SV_BanNum_f( void ) { cl->netchan.remoteAddress.ip[2], cl->netchan.remoteAddress.ip[3] ); Com_Printf("%s was banned from coming back\n", cl->name); } +#endif } /* diff --git a/src/server/sv_client.c b/src/server/sv_client.c index ba32299e..84ce86a0 100644 --- a/src/server/sv_client.c +++ b/src/server/sv_client.c @@ -77,138 +77,9 @@ void SV_GetChallenge( netadr_t from ) { i = oldest; } - // if they are on a lan address, send the challengeResponse immediately - if ( Sys_IsLANAddress( from ) ) { - challenge->pingTime = svs.time; - NET_OutOfBandPrint( NS_SERVER, from, "challengeResponse %i", challenge->challenge ); - return; - } - - // look up the authorize server's IP - if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) { - Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); - if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &svs.authorizeAddress ) ) { - Com_Printf( "Couldn't resolve address\n" ); - return; - } - svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE ); - Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, - svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1], - svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3], - BigShort( svs.authorizeAddress.port ) ); - } - - // if they have been challenging for a long time and we - // haven't heard anything from the authorize server, go ahead and - // let them in, assuming the id server is down - if ( svs.time - challenge->firstTime > AUTHORIZE_TIMEOUT ) { - Com_DPrintf( "authorize server timed out\n" ); - - challenge->pingTime = svs.time; - NET_OutOfBandPrint( NS_SERVER, challenge->adr, - "challengeResponse %i", challenge->challenge ); - return; - } - - // otherwise send their ip to the authorize server - if ( svs.authorizeAddress.type != NA_BAD ) { - cvar_t *fs; - char game[1024]; - - Com_DPrintf( "sending getIpAuthorize for %s\n", NET_AdrToString( from )); - - strcpy(game, BASEGAME); - fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); - if (fs && fs->string[0] != 0) { - strcpy(game, fs->string); - } - - // the 0 is for backwards compatibility with obsolete sv_allowanonymous flags - // getIpAuthorize 0 - NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress, - "getIpAuthorize %i %i.%i.%i.%i %s 0 %s", svs.challenges[i].challenge, - from.ip[0], from.ip[1], from.ip[2], from.ip[3], game, "0" ); - } -} - -/* -==================== -SV_AuthorizeIpPacket - -A packet has been returned from the authorize server. -If we have a challenge adr for that ip, send the -challengeResponse to it -==================== -*/ -void SV_AuthorizeIpPacket( netadr_t from ) { - int challenge; - int i; - char *s; - char *r; - char ret[1024]; - - if ( !NET_CompareBaseAdr( from, svs.authorizeAddress ) ) { - Com_Printf( "SV_AuthorizeIpPacket: not from authorize server\n" ); - return; - } - - challenge = atoi( Cmd_Argv( 1 ) ); - - for (i = 0 ; i < MAX_CHALLENGES ; i++) { - if ( svs.challenges[i].challenge == challenge ) { - break; - } - } - if ( i == MAX_CHALLENGES ) { - Com_Printf( "SV_AuthorizeIpPacket: challenge not found\n" ); - return; - } - - // send a packet back to the original client - svs.challenges[i].pingTime = svs.time; - s = Cmd_Argv( 2 ); - r = Cmd_Argv( 3 ); // reason - - if ( !Q_stricmp( s, "demo" ) ) { - if ( Cvar_VariableValue( "fs_restrict" ) ) { - // a demo client connecting to a demo server - NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, - "challengeResponse %i", svs.challenges[i].challenge ); - return; - } - // they are a demo client trying to connect to a real server - NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nServer is not a demo server\n" ); - // clear the challenge record so it won't timeout and let them through - Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) ); - return; - } - if ( !Q_stricmp( s, "accept" ) ) { - NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, - "challengeResponse %i", svs.challenges[i].challenge ); - return; - } - if ( !Q_stricmp( s, "unknown" ) ) { - if (!r) { - NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nAwaiting CD key authorization\n" ); - } else { - sprintf(ret, "print\n%s\n", r); - NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, ret ); - } - // clear the challenge record so it won't timeout and let them through - Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) ); - return; - } - - // authorization failed - if (!r) { - NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nSomeone is using this CD Key\n" ); - } else { - sprintf(ret, "print\n%s\n", r); - NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, ret ); - } - - // clear the challenge record so it won't timeout and let them through - Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) ); + // send the challengeResponse + challenge->pingTime = svs.time; + NET_OutOfBandPrint( NS_SERVER, from, "challengeResponse %i", challenge->challenge ); } /* @@ -218,11 +89,6 @@ SV_DirectConnect A "connect" OOB command has been received ================== */ - -#define PB_MESSAGE "PunkBuster Anti-Cheat software must be installed " \ - "and Enabled in order to join this server. An updated game patch can be downloaded from " \ - "www.idsoftware.com" - void SV_DirectConnect( netadr_t from ) { char userinfo[MAX_INFO_STRING]; int i; diff --git a/src/server/sv_init.c b/src/server/sv_init.c index e484edcc..3d08b6ea 100644 --- a/src/server/sv_init.c +++ b/src/server/sv_init.c @@ -442,9 +442,6 @@ void SV_SpawnServer( char *server, qboolean killBots ) { // load and spawn all other entities SV_InitGameProgs(); - // don't allow a map_restart if game is modified - sv_gametype->modified = qfalse; - // run a few frames to allow everything to settle for (i = 0;i < 3; i++) { @@ -572,10 +569,7 @@ void SV_Init (void) { SV_AddOperatorCommands (); // serverinfo vars - Cvar_Get ("dmflags", "0", CVAR_SERVERINFO); - Cvar_Get ("fraglimit", "20", CVAR_SERVERINFO); Cvar_Get ("timelimit", "0", CVAR_SERVERINFO); - sv_gametype = Cvar_Get ("g_gametype", "0", CVAR_SERVERINFO | CVAR_LATCH ); Cvar_Get ("sv_keywords", "", CVAR_SERVERINFO); Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_ROM); sv_mapname = Cvar_Get ("mapname", "nomap", CVAR_SERVERINFO | CVAR_ROM); diff --git a/src/server/sv_main.c b/src/server/sv_main.c index 6f96ac7f..1d658a60 100644 --- a/src/server/sv_main.c +++ b/src/server/sv_main.c @@ -48,7 +48,6 @@ cvar_t *sv_serverid; cvar_t *sv_maxRate; cvar_t *sv_minPing; cvar_t *sv_maxPing; -cvar_t *sv_gametype; cvar_t *sv_pure; cvar_t *sv_floodProtect; cvar_t *sv_lanForceRate; // dedicated 1 (LAN) server forces local client rates to 99999 (bug #491) @@ -220,7 +219,7 @@ but not on every player enter or exit. ================ */ #define HEARTBEAT_MSEC 300*1000 -#define HEARTBEAT_GAME "QuakeArena-1" +#define HEARTBEAT_GAME "Tremulous" void SV_MasterHeartbeat( void ) { static netadr_t adr[MAX_MASTER_SERVERS]; int i; @@ -400,7 +399,6 @@ void SVC_Info( netadr_t from ) { Info_SetValueForKey( infostring, "clients", va("%i", count) ); Info_SetValueForKey( infostring, "sv_maxclients", va("%i", sv_maxclients->integer - sv_privateClients->integer ) ); - Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) ); Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) ); if( sv_minPing->integer ) { @@ -531,8 +529,6 @@ void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) { SV_GetChallenge( from ); } else if (!Q_stricmp(c, "connect")) { SV_DirectConnect( from ); - } else if (!Q_stricmp(c, "ipAuthorize")) { - SV_AuthorizeIpPacket( from ); } else if (!Q_stricmp(c, "rcon")) { SVC_RemoteCommand( from, msg ); } else if (!Q_stricmp(c, "disconnect")) { diff --git a/src/server/sv_rankings.c b/src/server/sv_rankings.c index 023a7a2a..20d24fd4 100644 --- a/src/server/sv_rankings.c +++ b/src/server/sv_rankings.c @@ -86,7 +86,7 @@ void SV_RankBegin( char *gamekey ) assert( !s_rankings_active ); assert( s_ranked_players == NULL ); - if( sv_enableRankings->integer == 0 || Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) + if( sv_enableRankings->integer == 0 ) { s_rankings_active = qfalse; if( sv_rankingsActive->integer == 1 ) diff --git a/src/ui/ui_atoms.c b/src/ui/ui_atoms.c index 850a1ac2..64efa914 100644 --- a/src/ui/ui_atoms.c +++ b/src/ui/ui_atoms.c @@ -377,11 +377,6 @@ qboolean UI_ConsoleCommand( int realTime ) } } - if ( Q_stricmp (cmd, "ui_cdkey") == 0 ) { - //UI_CDKeyMenu_f(); - return qtrue; - } - return qfalse; } diff --git a/src/ui/ui_gameinfo.c b/src/ui/ui_gameinfo.c index 451e54af..ab3ce6da 100644 --- a/src/ui/ui_gameinfo.c +++ b/src/ui/ui_gameinfo.c @@ -123,6 +123,19 @@ static void UI_LoadArenasFromFile( char *filename ) { ui_numArenas += UI_ParseInfos( buf, MAX_ARENAS - ui_numArenas, &ui_arenaInfos[ui_numArenas] ); } +/* +================= +UI_MapNameCompare +================= +*/ +static int UI_MapNameCompare( const void *a, const void *b ) +{ + mapInfo *A = (mapInfo *)a; + mapInfo *B = (mapInfo *)b; + + return Q_stricmp( A->mapName, B->mapName ); +} + /* =============== UI_LoadArenas @@ -140,14 +153,6 @@ void UI_LoadArenas( void ) { ui_numArenas = 0; uiInfo.mapCount = 0; -/* trap_Cvar_Register( &arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM ); - if( *arenasFile.string ) { - UI_LoadArenasFromFile(arenasFile.string); - } - else { - UI_LoadArenasFromFile("scripts/arenas.txt"); - }*/ - // get all arenas from .arena files numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 1024 ); dirptr = dirlist; @@ -183,6 +188,8 @@ void UI_LoadArenas( void ) { if( uiInfo.mapCount >= MAX_MAPS ) break; } + + qsort( uiInfo.mapList, uiInfo.mapCount, sizeof( mapInfo ), UI_MapNameCompare ); } diff --git a/src/ui/ui_local.h b/src/ui/ui_local.h index 865ce927..d8340695 100644 --- a/src/ui/ui_local.h +++ b/src/ui/ui_local.h @@ -88,9 +88,6 @@ extern vmCvar_t ui_server14; extern vmCvar_t ui_server15; extern vmCvar_t ui_server16; -extern vmCvar_t ui_cdkey; -extern vmCvar_t ui_cdkeychecked; - extern vmCvar_t ui_captureLimit; extern vmCvar_t ui_fragLimit; extern vmCvar_t ui_gameType; @@ -431,13 +428,6 @@ extern void UI_CinematicsMenu_Cache( void ); extern void UI_ModsMenu( void ); extern void UI_ModsMenu_Cache( void ); -// -// ui_cdkey.c -// -extern void UI_CDKeyMenu( void ); -extern void UI_CDKeyMenu_Cache( void ); -extern void UI_CDKeyMenu_f( void ); - // // ui_playermodel.c // @@ -1059,8 +1049,6 @@ void trap_LAN_ResetPings(int n); int trap_LAN_ServerStatus( const char *serverAddress, char *serverStatus, int maxLen ); int trap_LAN_CompareServers( int source, int sortKey, int sortDir, int s1, int s2 ); int trap_MemoryRemaining( void ); -void trap_GetCDKey( char *buf, int buflen ); -void trap_SetCDKey( char *buf ); void trap_R_RegisterFont(const char *pFontname, int pointSize, fontInfo_t *font); void trap_S_StopBackgroundTrack( void ); void trap_S_StartBackgroundTrack( const char *intro, const char *loop); @@ -1071,7 +1059,6 @@ void trap_CIN_DrawCinematic (int handle); void trap_CIN_SetExtents (int handle, int x, int y, int w, int h); int trap_RealTime(qtime_t *qtime); void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ); -qboolean trap_VerifyCDKey( const char *key, const char *chksum); void trap_SetPbClStatus( int status ); diff --git a/src/ui/ui_main.c b/src/ui/ui_main.c index bf00c5fa..198a33f6 100644 --- a/src/ui/ui_main.c +++ b/src/ui/ui_main.c @@ -199,9 +199,6 @@ long vmMain( long command, long arg0, long arg1, long arg2, long arg3, case UI_DRAW_CONNECT_SCREEN: UI_DrawConnectScreen( arg0 ); return 0; - case UI_HASUNIQUECDKEY: // mod authors need to observe this - return qfalse; // bk010117 - change this to qfalse for mods! - } return -1; @@ -4002,37 +3999,6 @@ static void UI_RunMenuScript(char **args) { Controls_SetDefaults(); trap_Cvar_Set("com_introPlayed", "1" ); trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart\n" ); - } else if (Q_stricmp(name, "getCDKey") == 0) { - char out[17]; - trap_GetCDKey(buff, 17); - trap_Cvar_Set("cdkey1", ""); - trap_Cvar_Set("cdkey2", ""); - trap_Cvar_Set("cdkey3", ""); - trap_Cvar_Set("cdkey4", ""); - if (strlen(buff) == CDKEY_LEN) { - Q_strncpyz(out, buff, 5); - trap_Cvar_Set("cdkey1", out); - Q_strncpyz(out, buff + 4, 5); - trap_Cvar_Set("cdkey2", out); - Q_strncpyz(out, buff + 8, 5); - trap_Cvar_Set("cdkey3", out); - Q_strncpyz(out, buff + 12, 5); - trap_Cvar_Set("cdkey4", out); - } - - } else if (Q_stricmp(name, "verifyCDKey") == 0) { - buff[0] = '\0'; - Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey1")); - Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey2")); - Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey3")); - Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey4")); - trap_Cvar_Set("cdkey", buff); - if (trap_VerifyCDKey(buff, UI_Cvar_VariableString("cdkeychecksum"))) { - trap_Cvar_Set("ui_cdkeyvalid", "CD Key Appears to be valid."); - trap_SetCDKey(buff); - } else { - trap_Cvar_Set("ui_cdkeyvalid", "CD Key does not appear to be valid."); - } } else if (Q_stricmp(name, "loadArenas") == 0) { UI_LoadArenas(); UI_MapCountByGameType(qfalse); @@ -4601,10 +4567,6 @@ static void UI_BuildServerDisplayList(qboolean force) { // do motd updates here too trap_Cvar_VariableStringBuffer( "cl_motdString", uiInfo.serverStatus.motd, sizeof(uiInfo.serverStatus.motd) ); len = strlen(uiInfo.serverStatus.motd); - if (len == 0) { - strcpy(uiInfo.serverStatus.motd, "Welcome to Tremulous!"); - len = strlen(uiInfo.serverStatus.motd); - } if (len != uiInfo.serverStatus.motdLen) { uiInfo.serverStatus.motdLen = len; uiInfo.serverStatus.motdWidth = -1; @@ -4670,13 +4632,6 @@ static void UI_BuildServerDisplayList(qboolean force) { } } - //TA: only show tremulous servers - if( Q_stricmp( Info_ValueForKey( info, "game" ), "tremulous" ) != 0 ) - { - trap_LAN_MarkServerVisible( ui_netSource.integer, i, qfalse ); - continue; - } - // make sure we never add a favorite server twice if (ui_netSource.integer == AS_FAVORITES) { UI_RemoveServerFromDisplayList(i); @@ -5173,13 +5128,13 @@ static const char *UI_FeederItemText(float feederID, int index, int column, qhan return UI_SelectedMap(index, &actual); } else if (feederID == FEEDER_SERVERS) { if (index >= 0 && index < uiInfo.serverStatus.numDisplayServers) { - int ping, punkbuster; + int ping; if (lastColumn != column || lastTime > uiInfo.uiDC.realTime + 5000) { trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[index], info, MAX_STRING_CHARS); lastColumn = column; lastTime = uiInfo.uiDC.realTime; } - /*return "bah";*/ + ping = atoi(Info_ValueForKey(info, "ping")); if (ping == -1) { // if we ever see a ping that is out of date, do a server refresh @@ -5213,13 +5168,6 @@ static const char *UI_FeederItemText(float feederID, int index, int column, qhan } else { return Info_ValueForKey(info, "ping"); } - case SORT_PUNKBUSTER: - punkbuster = atoi(Info_ValueForKey(info, "punkbuster")); - if ( punkbuster ) { - return "Yes"; - } else { - return "No"; - } } } } else if (feederID == FEEDER_SERVERSTATUS) { @@ -5831,18 +5779,6 @@ void _UI_SetActiveMenu( uiMenuCommand_t menu ) { trap_Key_SetCatcher( KEYCATCH_UI ); Menus_ActivateByName("team"); return; - case UIMENU_NEED_CD: - // no cd check in TA - //trap_Key_SetCatcher( KEYCATCH_UI ); - //Menus_ActivateByName("needcd"); - //UI_ConfirmMenu( "Insert the CD", NULL, NeedCDAction ); - return; - case UIMENU_BAD_CD_KEY: - // no cd check in TA - //trap_Key_SetCatcher( KEYCATCH_UI ); - //Menus_ActivateByName("badcd"); - //UI_ConfirmMenu( "Bad CD Key", NULL, NeedCDKeyAction ); - return; case UIMENU_POSTGAME: //trap_Cvar_Set( "sv_killserver", "1" ); trap_Key_SetCatcher( KEYCATCH_UI ); @@ -5851,7 +5787,6 @@ void _UI_SetActiveMenu( uiMenuCommand_t menu ) { } Menus_CloseAll(); Menus_ActivateByName("endofgame"); - //UI_ConfirmMenu( "Bad CD Key", NULL, NeedCDKeyAction ); return; case UIMENU_INGAME: trap_Cvar_Set( "cl_paused", "1" ); @@ -6198,8 +6133,6 @@ vmCvar_t ui_server14; vmCvar_t ui_server15; vmCvar_t ui_server16; -vmCvar_t ui_cdkeychecked; - vmCvar_t ui_redteam; vmCvar_t ui_redteam1; vmCvar_t ui_redteam2; @@ -6321,7 +6254,6 @@ static cvarTable_t cvarTable[] = { { &ui_server14, "server14", "", CVAR_ARCHIVE }, { &ui_server15, "server15", "", CVAR_ARCHIVE }, { &ui_server16, "server16", "", CVAR_ARCHIVE }, - { &ui_cdkeychecked, "ui_cdkeychecked", "0", CVAR_ROM }, { &ui_new, "ui_new", "0", CVAR_TEMP }, { &ui_debug, "ui_debug", "0", CVAR_TEMP }, { &ui_initialized, "ui_initialized", "0", CVAR_TEMP }, @@ -6504,7 +6436,8 @@ static void UI_StartServerRefresh(qboolean full) qtime_t q; trap_RealTime(&q); - trap_Cvar_Set( va("ui_lastServerRefresh_%i", ui_netSource.integer), va("%s-%i, %i at %i:%i", MonthAbbrev[q.tm_mon],q.tm_mday, 1900+q.tm_year,q.tm_hour,q.tm_min)); + trap_Cvar_Set( va("ui_lastServerRefresh_%i", ui_netSource.integer), + va("%s-%i, %i at %i:%02i", MonthAbbrev[q.tm_mon],q.tm_mday, 1900+q.tm_year,q.tm_hour,q.tm_min)); if (!full) { UI_UpdatePendingPings(); diff --git a/src/ui/ui_public.h b/src/ui/ui_public.h index 2308256d..9e39d52b 100644 --- a/src/ui/ui_public.h +++ b/src/ui/ui_public.h @@ -89,8 +89,6 @@ typedef enum { UI_CVAR_REGISTER, UI_CVAR_UPDATE, UI_MEMORY_REMAINING, - UI_GET_CDKEY, - UI_SET_CDKEY, UI_R_REGISTERFONT, UI_R_MODELBOUNDS, UI_PC_ADD_GLOBAL_DEFINE, @@ -117,7 +115,6 @@ typedef enum { UI_CIN_DRAWCINEMATIC, UI_CIN_SETEXTENTS, UI_R_REMAP_SHADER, - UI_VERIFY_CDKEY, UI_LAN_SERVERSTATUS, UI_LAN_GETSERVERPING, UI_LAN_SERVERISVISIBLE, @@ -141,8 +138,6 @@ typedef enum { UIMENU_NONE, UIMENU_MAIN, UIMENU_INGAME, - UIMENU_NEED_CD, - UIMENU_BAD_CD_KEY, UIMENU_TEAM, UIMENU_POSTGAME } uiMenuCommand_t; @@ -152,7 +147,6 @@ typedef enum SORT_HOST, SORT_MAP, SORT_CLIENTS, - SORT_PUNKBUSTER, SORT_PING } serverSortField_t; @@ -183,9 +177,9 @@ typedef enum { UI_CONSOLE_COMMAND, // qboolean UI_ConsoleCommand( int realTime ); - UI_DRAW_CONNECT_SCREEN, + UI_DRAW_CONNECT_SCREEN // void UI_DrawConnectScreen( qboolean overlay ); - UI_HASUNIQUECDKEY + // if !overlay, the background will be drawn, otherwise it will be // overlayed over whatever the cgame has drawn. // a GetClientState syscall will be made to get the current strings diff --git a/src/ui/ui_syscalls.asm b/src/ui/ui_syscalls.asm index 54255f29..d84ce335 100644 --- a/src/ui/ui_syscalls.asm +++ b/src/ui/ui_syscalls.asm @@ -53,8 +53,6 @@ equ trap_LAN_GetPingInfo -50 equ trap_Cvar_Register -51 equ trap_Cvar_Update -52 equ trap_MemoryRemaining -53 -equ trap_GetCDKey -54 -equ trap_SetCDKey -55 equ trap_R_RegisterFont -56 equ trap_R_ModelBounds -57 equ trap_PC_AddGlobalDefine -58 @@ -81,7 +79,6 @@ equ trap_CIN_RunCinematic -78 equ trap_CIN_DrawCinematic -79 equ trap_CIN_SetExtents -80 equ trap_R_RemapShader -81 -equ trap_VerifyCDKey -82 equ trap_LAN_ServerStatus -83 equ trap_LAN_GetServerPing -84 equ trap_LAN_ServerIsVisible -85 diff --git a/src/ui/ui_syscalls.c b/src/ui/ui_syscalls.c index d0a17b77..3aba4d69 100644 --- a/src/ui/ui_syscalls.c +++ b/src/ui/ui_syscalls.c @@ -316,14 +316,6 @@ int trap_MemoryRemaining( void ) { return syscall( UI_MEMORY_REMAINING ); } -void trap_GetCDKey( char *buf, int buflen ) { - syscall( UI_GET_CDKEY, buf, buflen ); -} - -void trap_SetCDKey( char *buf ) { - syscall( UI_SET_CDKEY, buf ); -} - int trap_PC_AddGlobalDefine( char *define ) { return syscall( UI_PC_ADD_GLOBAL_DEFINE, define ); } @@ -390,10 +382,6 @@ void trap_R_RemapShader( const char *oldShader, const char *newShader, const ch syscall( UI_R_REMAP_SHADER, oldShader, newShader, timeOffset ); } -qboolean trap_VerifyCDKey( const char *key, const char *chksum) { - return syscall( UI_VERIFY_CDKEY, key, chksum); -} - void trap_SetPbClStatus( int status ) { syscall( UI_SET_PBCLSTATUS, status ); } diff --git a/src/unix/Makefile b/src/unix/Makefile index 69b3826c..6070a846 100644 --- a/src/unix/Makefile +++ b/src/unix/Makefile @@ -588,6 +588,9 @@ else # ifeq SunOS SHLIBLDFLAGS=-shared TARGETS=\ + $(B)/base/cgame$(ARCH).$(SHLIBEXT) \ + $(B)/base/qagame$(ARCH).$(SHLIBEXT) \ + $(B)/base/ui$(ARCH).$(SHLIBEXT) \ $(B)/$(PLATFORM)tremded endif #Linux @@ -650,11 +653,13 @@ release: build_release build_debug: B=$(BD) build_debug: makedirs tools - $(MAKE) targets B=$(BD) CFLAGS="$(CFLAGS) $(DEBUG_CFLAGS) $(DEPEND_CFLAGS)" + $(MAKE) targets B=$(BD) CFLAGS="$(CFLAGS) $(DEBUG_CFLAGS) $(DEPEND_CFLAGS)" + $(MAKE) -C ../master debug build_release: B=$(BR) build_release: makedirs tools - $(MAKE) targets B=$(BR) CFLAGS="$(CFLAGS) $(RELEASE_CFLAGS) $(DEPEND_CFLAGS)" + $(MAKE) targets B=$(BR) CFLAGS="$(CFLAGS) $(RELEASE_CFLAGS) $(DEPEND_CFLAGS)" + $(MAKE) -C ../master release #Build both debug and release builds all:build_debug build_release @@ -1558,6 +1563,7 @@ copyfiles: build_release clean:clean-debug clean-release $(MAKE) -C setup clean + $(MAKE) -C ../master clean clean2: if [ -d $(B) ];then (find $(B) -name '*.d' -exec rm {} \;)fi -- cgit