Connectionless server commands in eine eigene Datei

This commit is contained in:
Yamagi Burmeister 2010-11-26 08:12:50 +00:00
parent 5973a3c8e9
commit cb0159014d
3 changed files with 436 additions and 395 deletions

View file

@ -241,6 +241,7 @@ GAME_ABI_OBJS = \
# Server objects # Server objects
SERVER_OBJS = \ SERVER_OBJS = \
build/server/sv_cmd.o \ build/server/sv_cmd.o \
build/server/sv_conless.o \
build/server/sv_entities.o \ build/server/sv_entities.o \
build/server/sv_game.o \ build/server/sv_game.o \
build/server/sv_init.o \ build/server/sv_init.o \
@ -273,6 +274,7 @@ SDL_OBJS= \
# Dedicated server object # Dedicated server object
DEDICATED_SERVER_OBJS = \ DEDICATED_SERVER_OBJS = \
build/dedicated_server/sv_cmd.o \ build/dedicated_server/sv_cmd.o \
build/dedicated_server/sv_conless.o \
build/dedicated_server/sv_entities.o \ build/dedicated_server/sv_entities.o \
build/dedicated_server/sv_game.o \ build/dedicated_server/sv_game.o \
build/dedicated_server/sv_init.o \ build/dedicated_server/sv_init.o \
@ -607,6 +609,9 @@ build/gameabi/q_shared.o : src/game/baseq2/q_shared.c
build/server/sv_cmd.o : src/server/sv_cmd.c build/server/sv_cmd.o : src/server/sv_cmd.c
$(CC) $(CFLAGS_CLIENT) -o $@ -c $< $(CC) $(CFLAGS_CLIENT) -o $@ -c $<
build/server/sv_conless.o : src/server/sv_conless.c
$(CC) $(CFLAGS_CLIENT) -o $@ -c $<
build/server/sv_entities.o : src/server/sv_entities.c build/server/sv_entities.o : src/server/sv_entities.c
$(CC) $(CFLAGS_CLIENT) -o $@ -c $< $(CC) $(CFLAGS_CLIENT) -o $@ -c $<
@ -666,6 +671,9 @@ build/sdl/sound.o : src/sdl/sound.c
build/dedicated_server/sv_cmd.o : src/server/sv_cmd.c build/dedicated_server/sv_cmd.o : src/server/sv_cmd.c
$(CC) $(CFLAGS_DEDICATED_SERVER) -o $@ -c $< $(CC) $(CFLAGS_DEDICATED_SERVER) -o $@ -c $<
build/dedicated_server/sv_conless.o : src/server/sv_conless.c
$(CC) $(CFLAGS_DEDICATED_SERVER) -o $@ -c $<
build/dedicated_server/sv_entities.o : src/server/sv_entities.c build/dedicated_server/sv_entities.o : src/server/sv_entities.c
$(CC) $(CFLAGS_DEDICATED_SERVER) -o $@ -c $< $(CC) $(CFLAGS_DEDICATED_SERVER) -o $@ -c $<

427
src/server/sv_conless.c Normal file
View file

@ -0,0 +1,427 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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.
*
* =======================================================================
*
* Connectionless server commands.
*
* =======================================================================
*/
#include "header/server.h"
extern cvar_t *hostname;
extern cvar_t *rcon_password;
cvar_t *sv_reconnect_limit; /* minimum seconds between connect messages */
char * SV_StatusString ( void );
/*
* Responds with all the info that qplug or qspy can see
*/
void
SVC_Status ( void )
{
Netchan_OutOfBandPrint( NS_SERVER, net_from, "print\n%s", SV_StatusString() );
}
void
SVC_Ack ( void )
{
Com_Printf( "Ping acknowledge from %s\n", NET_AdrToString( net_from ) );
}
/*
* Responds with short info for broadcast scans
* The second parameter should be the current protocol version number.
*/
void
SVC_Info ( void )
{
char string [ 64 ];
int i, count;
int version;
if ( maxclients->value == 1 )
{
return; /* ignore in single player */
}
version = atoi( Cmd_Argv( 1 ) );
if ( version != PROTOCOL_VERSION )
{
Com_sprintf( string, sizeof ( string ), "%s: wrong version\n", hostname->string, sizeof ( string ) );
}
else
{
count = 0;
for ( i = 0; i < maxclients->value; i++ )
{
if ( svs.clients [ i ].state >= cs_connected )
{
count++;
}
}
Com_sprintf( string, sizeof ( string ), "%16s %8s %2i/%2i\n", hostname->string, sv.name, count, (int) maxclients->value );
}
Netchan_OutOfBandPrint( NS_SERVER, net_from, "info\n%s", string );
}
/*
* SVC_Ping
*/
void
SVC_Ping ( void )
{
Netchan_OutOfBandPrint( NS_SERVER, net_from, "ack" );
}
/*
* Returns a challenge number that can be used
* in a subsequent client_connect command.
* We do this to prevent denial of service attacks that
* flood the server with invalid connection IPs. With a
* challenge, they must give a valid IP address.
*/
void
SVC_GetChallenge ( void )
{
int i;
int oldest;
int oldestTime;
oldest = 0;
oldestTime = 0x7fffffff;
/* see if we already have a challenge for this ip */
for ( i = 0; i < MAX_CHALLENGES; i++ )
{
if ( NET_CompareBaseAdr( net_from, svs.challenges [ i ].adr ) )
{
break;
}
if ( svs.challenges [ i ].time < oldestTime )
{
oldestTime = svs.challenges [ i ].time;
oldest = i;
}
}
if ( i == MAX_CHALLENGES )
{
/* overwrite the oldest */
svs.challenges [ oldest ].challenge = rand() & 0x7fff;
svs.challenges [ oldest ].adr = net_from;
svs.challenges [ oldest ].time = curtime;
i = oldest;
}
/* send it back */
Netchan_OutOfBandPrint( NS_SERVER, net_from, "challenge %i", svs.challenges [ i ].challenge );
}
/*
* A connection request that did not come from the master
*/
void
SVC_DirectConnect ( void )
{
char userinfo [ MAX_INFO_STRING ];
netadr_t adr;
int i;
client_t *cl, *newcl;
client_t temp;
edict_t *ent;
int edictnum;
int version;
int qport;
int challenge;
adr = net_from;
Com_DPrintf( "SVC_DirectConnect ()\n" );
version = atoi( Cmd_Argv( 1 ) );
if ( version != PROTOCOL_VERSION )
{
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nServer is version %4.2f.\n", VERSION );
Com_DPrintf( " rejected connect from version %i\n", version );
return;
}
qport = atoi( Cmd_Argv( 2 ) );
challenge = atoi( Cmd_Argv( 3 ) );
strncpy( userinfo, Cmd_Argv( 4 ), sizeof ( userinfo ) - 1 );
userinfo [ sizeof ( userinfo ) - 1 ] = 0;
/* force the IP key/value pair so the game can filter based on ip */
Info_SetValueForKey( userinfo, "ip", NET_AdrToString( net_from ) );
/* attractloop servers are ONLY for local clients */
if ( sv.attractloop )
{
if ( !NET_IsLocalAddress( adr ) )
{
Com_Printf( "Remote connect in attract loop. Ignored.\n" );
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nConnection refused.\n" );
return;
}
}
/* see if the challenge is valid */
if ( !NET_IsLocalAddress( adr ) )
{
for ( i = 0; i < MAX_CHALLENGES; i++ )
{
if ( NET_CompareBaseAdr( net_from, svs.challenges [ i ].adr ) )
{
if ( challenge == svs.challenges [ i ].challenge )
{
break; /* good */
}
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nBad challenge.\n" );
return;
}
}
if ( i == MAX_CHALLENGES )
{
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nNo challenge for address.\n" );
return;
}
}
newcl = &temp;
memset( newcl, 0, sizeof ( client_t ) );
/* if there is already a slot for this ip, reuse it */
for ( i = 0, cl = svs.clients; i < maxclients->value; i++, cl++ )
{
if ( cl->state < cs_connected )
{
continue;
}
if ( NET_CompareBaseAdr( adr, cl->netchan.remote_address ) &&
( ( cl->netchan.qport == qport ) ||
( adr.port == cl->netchan.remote_address.port ) ) )
{
if ( !NET_IsLocalAddress( adr ) && ( ( svs.realtime - cl->lastconnect ) < ( (int) sv_reconnect_limit->value * 1000 ) ) )
{
Com_DPrintf( "%s:reconnect rejected : too soon\n", NET_AdrToString( adr ) );
return;
}
Com_Printf( "%s:reconnect\n", NET_AdrToString( adr ) );
newcl = cl;
goto gotnewcl;
}
}
/* find a client slot */
newcl = NULL;
for ( i = 0, cl = svs.clients; i < maxclients->value; i++, cl++ )
{
if ( cl->state == cs_free )
{
newcl = cl;
break;
}
}
if ( !newcl )
{
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nServer is full.\n" );
Com_DPrintf( "Rejected a connection.\n" );
return;
}
gotnewcl:
/* build a new connection
accept the new client
this is the only place a client_t is ever initialized */
*newcl = temp;
sv_client = newcl;
edictnum = ( newcl - svs.clients ) + 1;
ent = EDICT_NUM( edictnum );
newcl->edict = ent;
newcl->challenge = challenge; /* save challenge for checksumming */
/* get the game a chance to reject this connection or modify the userinfo */
if ( !( ge->ClientConnect( ent, userinfo ) ) )
{
if ( *Info_ValueForKey( userinfo, "rejmsg" ) )
{
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\n%s\nConnection refused.\n",
Info_ValueForKey( userinfo, "rejmsg" ) );
}
else
{
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nConnection refused.\n" );
}
Com_DPrintf( "Game rejected a connection.\n" );
return;
}
/* parse some info from the info strings */
strncpy( newcl->userinfo, userinfo, sizeof ( newcl->userinfo ) - 1 );
SV_UserinfoChanged( newcl );
/* send the connect packet to the client */
Netchan_OutOfBandPrint( NS_SERVER, adr, "client_connect" );
Netchan_Setup( NS_SERVER, &newcl->netchan, adr, qport );
newcl->state = cs_connected;
SZ_Init( &newcl->datagram, newcl->datagram_buf, sizeof ( newcl->datagram_buf ) );
newcl->datagram.allowoverflow = true;
newcl->lastmessage = svs.realtime; /* don't timeout */
newcl->lastconnect = svs.realtime;
}
int
Rcon_Validate ( void )
{
if ( !strlen( rcon_password->string ) )
{
return ( 0 );
}
if ( strcmp( Cmd_Argv( 1 ), rcon_password->string ) )
{
return ( 0 );
}
return ( 1 );
}
/*
* A client issued an rcon command.
* Shift down the remaining args
* Redirect all printfs
*/
void
SVC_RemoteCommand ( void )
{
int i;
char remaining [ 1024 ];
i = Rcon_Validate();
if ( i == 0 )
{
Com_Printf( "Bad rcon from %s:\n%s\n", NET_AdrToString( net_from ), net_message.data + 4 );
}
else
{
Com_Printf( "Rcon from %s:\n%s\n", NET_AdrToString( net_from ), net_message.data + 4 );
}
Com_BeginRedirect( RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect );
if ( !Rcon_Validate() )
{
Com_Printf( "Bad rcon_password.\n" );
}
else
{
remaining [ 0 ] = 0;
for ( i = 2; i < Cmd_Argc(); i++ )
{
strcat( remaining, Cmd_Argv( i ) );
strcat( remaining, " " );
}
Cmd_ExecuteString( remaining );
}
Com_EndRedirect();
}
/*
* A connectionless packet has four leading 0xff
* characters to distinguish it from a game channel.
* Clients that are in the game can still send
* connectionless packets.
*/
void
SV_ConnectionlessPacket ( void )
{
char *s;
char *c;
MSG_BeginReading( &net_message );
MSG_ReadLong( &net_message ); /* skip the -1 marker */
s = MSG_ReadStringLine( &net_message );
Cmd_TokenizeString( s, false );
c = Cmd_Argv( 0 );
Com_DPrintf( "Packet %s : %s\n", NET_AdrToString( net_from ), c );
if ( !strcmp( c, "ping" ) )
{
SVC_Ping();
}
else if ( !strcmp( c, "ack" ) )
{
SVC_Ack();
}
else if ( !strcmp( c, "status" ) )
{
SVC_Status();
}
else if ( !strcmp( c, "info" ) )
{
SVC_Info();
}
else if ( !strcmp( c, "getchallenge" ) )
{
SVC_GetChallenge();
}
else if ( !strcmp( c, "connect" ) )
{
SVC_DirectConnect();
}
else if ( !strcmp( c, "rcon" ) )
{
SVC_RemoteCommand();
}
else
{
Com_Printf( "bad connectionless packet from %s:\n%s\n",
NET_AdrToString( net_from ), s );
}
}

View file

@ -49,9 +49,9 @@ cvar_t *maxclients; /* FIXME: rename sv_maxclients */
cvar_t *sv_showclamp; cvar_t *sv_showclamp;
cvar_t *hostname; cvar_t *hostname;
cvar_t *public_server; /* should heartbeats be sent */ cvar_t *public_server; /* should heartbeats be sent */
cvar_t *sv_reconnect_limit; /* minimum seconds between connect messages */
void Master_Shutdown ( void ); void Master_Shutdown ( void );
void SV_ConnectionlessPacket ( void );
/* /*
* Called when the player is totally leaving the server, either willingly * Called when the player is totally leaving the server, either willingly
@ -121,400 +121,6 @@ SV_StatusString ( void )
return ( status ); return ( status );
} }
/*
* Responds with all the info that qplug or qspy can see
*/
void
SVC_Status ( void )
{
Netchan_OutOfBandPrint( NS_SERVER, net_from, "print\n%s", SV_StatusString() );
}
void
SVC_Ack ( void )
{
Com_Printf( "Ping acknowledge from %s\n", NET_AdrToString( net_from ) );
}
/*
* Responds with short info for broadcast scans
* The second parameter should be the current protocol version number.
*/
void
SVC_Info ( void )
{
char string [ 64 ];
int i, count;
int version;
if ( maxclients->value == 1 )
{
return; /* ignore in single player */
}
version = atoi( Cmd_Argv( 1 ) );
if ( version != PROTOCOL_VERSION )
{
Com_sprintf( string, sizeof ( string ), "%s: wrong version\n", hostname->string, sizeof ( string ) );
}
else
{
count = 0;
for ( i = 0; i < maxclients->value; i++ )
{
if ( svs.clients [ i ].state >= cs_connected )
{
count++;
}
}
Com_sprintf( string, sizeof ( string ), "%16s %8s %2i/%2i\n", hostname->string, sv.name, count, (int) maxclients->value );
}
Netchan_OutOfBandPrint( NS_SERVER, net_from, "info\n%s", string );
}
/*
* SVC_Ping
*/
void
SVC_Ping ( void )
{
Netchan_OutOfBandPrint( NS_SERVER, net_from, "ack" );
}
/*
* Returns a challenge number that can be used
* in a subsequent client_connect command.
* We do this to prevent denial of service attacks that
* flood the server with invalid connection IPs. With a
* challenge, they must give a valid IP address.
*/
void
SVC_GetChallenge ( void )
{
int i;
int oldest;
int oldestTime;
oldest = 0;
oldestTime = 0x7fffffff;
/* see if we already have a challenge for this ip */
for ( i = 0; i < MAX_CHALLENGES; i++ )
{
if ( NET_CompareBaseAdr( net_from, svs.challenges [ i ].adr ) )
{
break;
}
if ( svs.challenges [ i ].time < oldestTime )
{
oldestTime = svs.challenges [ i ].time;
oldest = i;
}
}
if ( i == MAX_CHALLENGES )
{
/* overwrite the oldest */
svs.challenges [ oldest ].challenge = rand() & 0x7fff;
svs.challenges [ oldest ].adr = net_from;
svs.challenges [ oldest ].time = curtime;
i = oldest;
}
/* send it back */
Netchan_OutOfBandPrint( NS_SERVER, net_from, "challenge %i", svs.challenges [ i ].challenge );
}
/*
* A connection request that did not come from the master
*/
void
SVC_DirectConnect ( void )
{
char userinfo [ MAX_INFO_STRING ];
netadr_t adr;
int i;
client_t *cl, *newcl;
client_t temp;
edict_t *ent;
int edictnum;
int version;
int qport;
int challenge;
adr = net_from;
Com_DPrintf( "SVC_DirectConnect ()\n" );
version = atoi( Cmd_Argv( 1 ) );
if ( version != PROTOCOL_VERSION )
{
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nServer is version %4.2f.\n", VERSION );
Com_DPrintf( " rejected connect from version %i\n", version );
return;
}
qport = atoi( Cmd_Argv( 2 ) );
challenge = atoi( Cmd_Argv( 3 ) );
strncpy( userinfo, Cmd_Argv( 4 ), sizeof ( userinfo ) - 1 );
userinfo [ sizeof ( userinfo ) - 1 ] = 0;
/* force the IP key/value pair so the game can filter based on ip */
Info_SetValueForKey( userinfo, "ip", NET_AdrToString( net_from ) );
/* attractloop servers are ONLY for local clients */
if ( sv.attractloop )
{
if ( !NET_IsLocalAddress( adr ) )
{
Com_Printf( "Remote connect in attract loop. Ignored.\n" );
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nConnection refused.\n" );
return;
}
}
/* see if the challenge is valid */
if ( !NET_IsLocalAddress( adr ) )
{
for ( i = 0; i < MAX_CHALLENGES; i++ )
{
if ( NET_CompareBaseAdr( net_from, svs.challenges [ i ].adr ) )
{
if ( challenge == svs.challenges [ i ].challenge )
{
break; /* good */
}
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nBad challenge.\n" );
return;
}
}
if ( i == MAX_CHALLENGES )
{
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nNo challenge for address.\n" );
return;
}
}
newcl = &temp;
memset( newcl, 0, sizeof ( client_t ) );
/* if there is already a slot for this ip, reuse it */
for ( i = 0, cl = svs.clients; i < maxclients->value; i++, cl++ )
{
if ( cl->state < cs_connected )
{
continue;
}
if ( NET_CompareBaseAdr( adr, cl->netchan.remote_address ) &&
( ( cl->netchan.qport == qport ) ||
( adr.port == cl->netchan.remote_address.port ) ) )
{
if ( !NET_IsLocalAddress( adr ) && ( ( svs.realtime - cl->lastconnect ) < ( (int) sv_reconnect_limit->value * 1000 ) ) )
{
Com_DPrintf( "%s:reconnect rejected : too soon\n", NET_AdrToString( adr ) );
return;
}
Com_Printf( "%s:reconnect\n", NET_AdrToString( adr ) );
newcl = cl;
goto gotnewcl;
}
}
/* find a client slot */
newcl = NULL;
for ( i = 0, cl = svs.clients; i < maxclients->value; i++, cl++ )
{
if ( cl->state == cs_free )
{
newcl = cl;
break;
}
}
if ( !newcl )
{
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nServer is full.\n" );
Com_DPrintf( "Rejected a connection.\n" );
return;
}
gotnewcl:
/* build a new connection
accept the new client
this is the only place a client_t is ever initialized */
*newcl = temp;
sv_client = newcl;
edictnum = ( newcl - svs.clients ) + 1;
ent = EDICT_NUM( edictnum );
newcl->edict = ent;
newcl->challenge = challenge; /* save challenge for checksumming */
/* get the game a chance to reject this connection or modify the userinfo */
if ( !( ge->ClientConnect( ent, userinfo ) ) )
{
if ( *Info_ValueForKey( userinfo, "rejmsg" ) )
{
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\n%s\nConnection refused.\n",
Info_ValueForKey( userinfo, "rejmsg" ) );
}
else
{
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nConnection refused.\n" );
}
Com_DPrintf( "Game rejected a connection.\n" );
return;
}
/* parse some info from the info strings */
strncpy( newcl->userinfo, userinfo, sizeof ( newcl->userinfo ) - 1 );
SV_UserinfoChanged( newcl );
/* send the connect packet to the client */
Netchan_OutOfBandPrint( NS_SERVER, adr, "client_connect" );
Netchan_Setup( NS_SERVER, &newcl->netchan, adr, qport );
newcl->state = cs_connected;
SZ_Init( &newcl->datagram, newcl->datagram_buf, sizeof ( newcl->datagram_buf ) );
newcl->datagram.allowoverflow = true;
newcl->lastmessage = svs.realtime; /* don't timeout */
newcl->lastconnect = svs.realtime;
}
int
Rcon_Validate ( void )
{
if ( !strlen( rcon_password->string ) )
{
return ( 0 );
}
if ( strcmp( Cmd_Argv( 1 ), rcon_password->string ) )
{
return ( 0 );
}
return ( 1 );
}
/*
* A client issued an rcon command.
* Shift down the remaining args
* Redirect all printfs
*/
void
SVC_RemoteCommand ( void )
{
int i;
char remaining [ 1024 ];
i = Rcon_Validate();
if ( i == 0 )
{
Com_Printf( "Bad rcon from %s:\n%s\n", NET_AdrToString( net_from ), net_message.data + 4 );
}
else
{
Com_Printf( "Rcon from %s:\n%s\n", NET_AdrToString( net_from ), net_message.data + 4 );
}
Com_BeginRedirect( RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect );
if ( !Rcon_Validate() )
{
Com_Printf( "Bad rcon_password.\n" );
}
else
{
remaining [ 0 ] = 0;
for ( i = 2; i < Cmd_Argc(); i++ )
{
strcat( remaining, Cmd_Argv( i ) );
strcat( remaining, " " );
}
Cmd_ExecuteString( remaining );
}
Com_EndRedirect();
}
/*
* A connectionless packet has four leading 0xff
* characters to distinguish it from a game channel.
* Clients that are in the game can still send
* connectionless packets.
*/
void
SV_ConnectionlessPacket ( void )
{
char *s;
char *c;
MSG_BeginReading( &net_message );
MSG_ReadLong( &net_message ); /* skip the -1 marker */
s = MSG_ReadStringLine( &net_message );
Cmd_TokenizeString( s, false );
c = Cmd_Argv( 0 );
Com_DPrintf( "Packet %s : %s\n", NET_AdrToString( net_from ), c );
if ( !strcmp( c, "ping" ) )
{
SVC_Ping();
}
else if ( !strcmp( c, "ack" ) )
{
SVC_Ack();
}
else if ( !strcmp( c, "status" ) )
{
SVC_Status();
}
else if ( !strcmp( c, "info" ) )
{
SVC_Info();
}
else if ( !strcmp( c, "getchallenge" ) )
{
SVC_GetChallenge();
}
else if ( !strcmp( c, "connect" ) )
{
SVC_DirectConnect();
}
else if ( !strcmp( c, "rcon" ) )
{
SVC_RemoteCommand();
}
else
{
Com_Printf( "bad connectionless packet from %s:\n%s\n",
NET_AdrToString( net_from ), s );
}
}
/* ============================================================================ */
/* /*
* Updates the cl->ping variables * Updates the cl->ping variables
*/ */