mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2024-11-22 20:51:31 +00:00
Connectionless server commands in eine eigene Datei
This commit is contained in:
parent
5973a3c8e9
commit
cb0159014d
3 changed files with 436 additions and 395 deletions
8
Makefile
8
Makefile
|
@ -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
427
src/server/sv_conless.c
Normal 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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue