📄 sv_client.c
字号:
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code 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.
Quake III Arena source code 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 Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// sv_client.c -- server code for dealing with clients
#include "server.h"
static void SV_CloseDownload( client_t *cl );
/*
=================
SV_GetChallenge
A "getchallenge" OOB command has been received
Returns a challenge number that can be used
in a subsequent connectResponse 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.
If we are authorizing, a challenge request will cause a packet
to be sent to the authorize server.
When an authorizeip is returned, a challenge response will be
sent to that ip.
=================
*/
void SV_GetChallenge( netadr_t from ) {
int i;
int oldest;
int oldestTime;
challenge_t *challenge;
// ignore if we are in single player
if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) {
return;
}
oldest = 0;
oldestTime = 0x7fffffff;
// see if we already have a challenge for this ip
challenge = &svs.challenges[0];
for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++) {
if ( !challenge->connected && NET_CompareAdr( from, challenge->adr ) ) {
break;
}
if ( challenge->time < oldestTime ) {
oldestTime = challenge->time;
oldest = i;
}
}
if (i == MAX_CHALLENGES) {
// this is the first time this client has asked for a challenge
challenge = &svs.challenges[oldest];
challenge->challenge = ( (rand() << 16) ^ rand() ) ^ svs.time;
challenge->adr = from;
challenge->firstTime = svs.time;
challenge->time = svs.time;
challenge->connected = qfalse;
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 <challenge> <IP> <game> 0 <auth-flag>
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, sv_strictAuth->string );
}
}
/*
====================
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] ) );
}
/*
==================
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;
client_t *cl, *newcl;
MAC_STATIC client_t temp;
sharedEntity_t *ent;
int clientNum;
int version;
int qport;
int challenge;
char *password;
int startIndex;
char *denied;
int count;
Com_DPrintf ("SVC_DirectConnect ()\n");
Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) );
version = atoi( Info_ValueForKey( userinfo, "protocol" ) );
if ( version != PROTOCOL_VERSION ) {
NET_OutOfBandPrint( NS_SERVER, from, "print\nServer uses protocol version %i.\n", PROTOCOL_VERSION );
Com_DPrintf (" rejected connect from version %i\n", version);
return;
}
challenge = atoi( Info_ValueForKey( userinfo, "challenge" ) );
qport = atoi( Info_ValueForKey( userinfo, "qport" ) );
// quick reject
for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
if ( cl->state == CS_FREE ) {
continue;
}
if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress )
&& ( cl->netchan.qport == qport
|| from.port == cl->netchan.remoteAddress.port ) ) {
if (( svs.time - cl->lastConnectTime)
< (sv_reconnectlimit->integer * 1000)) {
Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (from));
return;
}
break;
}
}
// see if the challenge is valid (LAN clients don't need to challenge)
if ( !NET_IsLocalAddress (from) ) {
int ping;
for (i=0 ; i<MAX_CHALLENGES ; i++) {
if (NET_CompareAdr(from, svs.challenges[i].adr)) {
if ( challenge == svs.challenges[i].challenge ) {
break; // good
}
}
}
if (i == MAX_CHALLENGES) {
NET_OutOfBandPrint( NS_SERVER, from, "print\nNo or bad challenge for address.\n" );
return;
}
// force the IP key/value pair so the game can filter based on ip
Info_SetValueForKey( userinfo, "ip", NET_AdrToString( from ) );
ping = svs.time - svs.challenges[i].pingTime;
Com_Printf( "Client %i connecting with %i challenge ping\n", i, ping );
svs.challenges[i].connected = qtrue;
// never reject a LAN client based on ping
if ( !Sys_IsLANAddress( from ) ) {
if ( sv_minPing->value && ping < sv_minPing->value ) {
// don't let them keep trying until they get a big delay
NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is for high pings only\n" );
Com_DPrintf ("Client %i rejected on a too low ping\n", i);
// reset the address otherwise their ping will keep increasing
// with each connect message and they'd eventually be able to connect
svs.challenges[i].adr.port = 0;
return;
}
if ( sv_maxPing->value && ping > sv_maxPing->value ) {
NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is for low pings only\n" );
Com_DPrintf ("Client %i rejected on a too high ping\n", i);
return;
}
}
} else {
// force the "ip" info key to "localhost"
Info_SetValueForKey( userinfo, "ip", "localhost" );
}
newcl = &temp;
Com_Memset (newcl, 0, sizeof(client_t));
// if there is already a slot for this ip, reuse it
for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
if ( cl->state == CS_FREE ) {
continue;
}
if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress )
&& ( cl->netchan.qport == qport
|| from.port == cl->netchan.remoteAddress.port ) ) {
Com_Printf ("%s:reconnect\n", NET_AdrToString (from));
newcl = cl;
// this doesn't work because it nukes the players userinfo
// // disconnect the client from the game first so any flags the
// // player might have are dropped
// VM_Call( gvm, GAME_CLIENT_DISCONNECT, newcl - svs.clients );
//
goto gotnewcl;
}
}
// find a client slot
// if "sv_privateClients" is set > 0, then that number
// of client slots will be reserved for connections that
// have "password" set to the value of "sv_privatePassword"
// Info requests will report the maxclients as if the private
// slots didn't exist, to prevent people from trying to connect
// to a full server.
// This is to allow us to reserve a couple slots here on our
// servers so we can play without having to kick people.
// check for privateClient password
password = Info_ValueForKey( userinfo, "password" );
if ( !strcmp( password, sv_privatePassword->string ) ) {
startIndex = 0;
} else {
// skip past the reserved slots
startIndex = sv_privateClients->integer;
}
newcl = NULL;
for ( i = startIndex; i < sv_maxclients->integer ; i++ ) {
cl = &svs.clients[i];
if (cl->state == CS_FREE) {
newcl = cl;
break;
}
}
if ( !newcl ) {
if ( NET_IsLocalAddress( from ) ) {
count = 0;
for ( i = startIndex; i < sv_maxclients->integer ; i++ ) {
cl = &svs.clients[i];
if (cl->netchan.remoteAddress.type == NA_BOT) {
count++;
}
}
// if they're all bots
if (count >= sv_maxclients->integer - startIndex) {
SV_DropClient(&svs.clients[sv_maxclients->integer - 1], "only bots on server");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -