Example #1
0
/*
====================
CL_PlayDemo_f

play [demoname]
====================
*/
void CL_PlayDemo_f (void)
{
	char	name[256];

	if (Cmd_Argc() != 2)
	{
		Con_Printf ("play <demoname> : plays a demo\n");
		return;
	}

//
// disconnect from server
//
	CL_Disconnect ();
	
//
// open the demo file
//
	strcpy (name, Cmd_Argv(1));
	COM_DefaultExtension (name, ".qwd");

	Con_Printf ("Playing demo from %s.\n", name);
	COM_FOpenFile (name, &cls.demofile);
	if (!cls.demofile)
	{
		Con_Printf ("ERROR: couldn't open.\n");
		cls.demonum = -1;		// stop demo loop
		return;
	}

	cls.demoplayback = true;
	cls.state = ca_demostart;
	Netchan_Setup (&cls.netchan, net_from, 0);
	realtime = 0;
}
Example #2
0
/*
* TV_Upstream_ClientConnectPacket
* ClientConnect in client code
*/
static void TV_Upstream_ClientConnectPacket( upstream_t *upstream, msg_t *msg ) {
	if( upstream->state != CA_CONNECTING ) {
		return;
	}

	Netchan_Setup( &upstream->netchan, upstream->socket, &upstream->serveraddress, Netchan_GamePort() );
	upstream->state = CA_HANDSHAKE;
	TV_Upstream_AddReliableCommand( upstream, "new" );

	Com_Printf( "%s" S_COLOR_WHITE ": Connected\n", upstream->name );
}
Example #3
0
File: sv_bot.cpp Project: luaman/zq
edict_t *SV_CreateBot ()
{
	int			i, numclients;
	client_t	*cl, *newcl;
	edict_t		*ent;

	numclients = 0;
	newcl = NULL;

	for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) {
		if (cl->state == cs_free) {
			if (!newcl)
				newcl = cl;
			continue;
		}
		if (!cl->spectator)
			numclients++;
	}

	if (numclients >= maxclients.value || !newcl)
		return sv.edicts;		// all player spots full, return world

	newcl->clear();
	newcl->state = cs_spawned;
	newcl->bot = true;
	newcl->userid = SV_GenerateUserID();
	newcl->extensions = CLIENT_EXTENSIONS;	// bots always use latest ZQuake :-)

	// init a bogus network connection
	SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf));
	newcl->datagram.allowoverflow = true;
	Netchan_Setup (NS_SERVER, &newcl->netchan, net_null, 0);

	// set up the edict
	ent = EDICT_NUM((newcl - svs.clients) + 1);
	newcl->edict = ent;

//	Com_DPrintf ("Bot %s connected\n", newcl->name.c_str());

	SetUpClientEdict (newcl, ent);

	return ent;
}
Example #4
0
void CheckQizmoCompletion (void)
{
	DWORD ExitCode;

	if (!hQizmoProcess)
		return;

	if (!GetExitCodeProcess (hQizmoProcess, &ExitCode)) {
		Com_Printf ("WARINING: GetExitCodeProcess failed\n");
		hQizmoProcess = NULL;
		qwz_unpacking = false;
		qwz_playback = false;
		cls.demoplayback = cls.timedemo = false;
		StopQWZPlayback ();
		return;
	}
	
	if (ExitCode == STILL_ACTIVE)
		return;

	hQizmoProcess = NULL;

	if (!qwz_unpacking || !cls.demoplayback) {
		StopQWZPlayback ();
		return;
	}
	
	qwz_unpacking = false;
	
	cls.demofile = fopen (tempqwd_name, "rb");
	if (!cls.demofile) {
		Com_Printf ("Couldn't open %s\n", tempqwd_name);
		qwz_playback = false;
		cls.demoplayback = cls.timedemo = false;
		return;
	}

	// start playback
	cls.demoplayback = true;
	cls.state = ca_demostart;
	Netchan_Setup (NS_CLIENT, &cls.netchan, net_null, 0);
	cls.demotime = 0;
}
Example #5
0
/*
==================
SV_DirectConnect

A "connect" OOB command has been received
==================
*/
void SV_DirectConnect( netadr_t from, const Cmd::Args& args )
{
	if ( args.Argc() < 2 )
	{
		return;
	}

	Log::Debug( "SVC_DirectConnect ()" );

	InfoMap userinfo = InfoStringToMap(args.Argv(1));

	// DHM - Nerve :: Update Server allows any protocol to connect
	// NOTE TTimo: but we might need to store the protocol around for potential non http/ftp clients
	int version = atoi( userinfo["protocol"].c_str() );

	if ( version != PROTOCOL_VERSION )
	{
		Net::OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\nServer uses protocol version %i (yours is %i).", PROTOCOL_VERSION, version );
		Log::Debug( "    rejected connect from version %i", version );
		return;
	}

	int qport = atoi( userinfo["qport"].c_str() );

	auto clients_begin = svs.clients;
	auto clients_end = clients_begin + sv_maxclients->integer;

	client_t* reconnecting = std::find_if(clients_begin, clients_end,
		[&from, qport](const client_t& client)
		{
			return NET_CompareBaseAdr( from, client.netchan.remoteAddress )
		     && ( client.netchan.qport == qport || from.port == client.netchan.remoteAddress.port );
		}
	);

	if ( reconnecting != clients_end &&
		svs.time - reconnecting->lastConnectTime < sv_reconnectlimit->integer * 1000 )
	{
		Log::Debug( "%s: reconnect rejected: too soon", NET_AdrToString( from ) );
		return;
	}


	if ( NET_IsLocalAddress( from ) )
	{
		userinfo["ip"] = "loopback";
	}
	else
	{
		// see if the challenge is valid (local clients don't need to challenge)
		Challenge::Duration ping_duration;
		if ( !ChallengeManager::MatchString( from, userinfo["challenge"], &ping_duration ) )
		{
			Net::OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n[err_dialog]No or bad challenge for address." );
			return;
		}

		userinfo["ip"] = NET_AdrToString( from );
	}

	client_t *new_client = nullptr;

	// if there is already a slot for this IP address, reuse it
	if ( reconnecting != clients_end )
	{
		Log::Notice( "%s:reconnect\n", NET_AdrToString( from ) );
		new_client = reconnecting;
	}
	else
	{
		// 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

		int startIndex = 0;
		if ( userinfo["password"] != sv_privatePassword->string )
		{
			// skip past the reserved slots
			startIndex = sv_privateClients->integer;
		}

		new_client = std::find_if(clients_begin, clients_end,
			[](const client_t& client) {
				return client.state == clientState_t::CS_FREE;
		});

		if ( new_client == clients_end )
		{
			if ( NET_IsLocalAddress( from ) )
			{
				int count = std::count_if(clients_begin+startIndex, clients_end,
					[](const client_t& client) { return SV_IsBot(&client); }
				);

				// if they're all bots
				if ( count >= sv_maxclients->integer - startIndex )
				{
					SV_DropClient( &svs.clients[ sv_maxclients->integer - 1 ], "only bots on server" );
					new_client = &svs.clients[ sv_maxclients->integer - 1 ];
				}
				else
				{
					Com_Error( errorParm_t::ERR_FATAL, "server is full on local connect" );
				}
			}
			else
			{
				Net::OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n%s", sv_fullmsg->string );
				Log::Debug( "Rejected a connection." );
				return;
			}
		}
	}

	// build a new connection
	// accept the new client
	// this is the only place a client_t is ever initialized
	memset( new_client, 0, sizeof( client_t ) );
	int clientNum = new_client - svs.clients;


#ifdef HAVE_GEOIP
		const char * country = NET_GeoIP_Country( &from );

		if ( country )
		{
			Log::Notice( "Client %i connecting from %s\n", clientNum, country );
			userinfo["geoip"] = country;
		}
		else
		{
			Log::Notice( "Client %i connecting from somewhere unknown\n", clientNum );
		}
#else
		Log::Notice( "Client %i connecting\n", clientNum );
#endif

	new_client->gentity = SV_GentityNum( clientNum );
	new_client->gentity->r.svFlags = 0;

	// save the address
	Netchan_Setup( netsrc_t::NS_SERVER, &new_client->netchan, from, qport );
	// init the netchan queue

	// Save the pubkey
	Q_strncpyz( new_client->pubkey, userinfo["pubkey"].c_str(), sizeof( new_client->pubkey ) );
	userinfo.erase("pubkey");
	// save the userinfo
	Q_strncpyz( new_client->userinfo, InfoMapToString(userinfo).c_str(), sizeof( new_client->userinfo ) );

	// get the game a chance to reject this connection or modify the userinfo
	char reason[ MAX_STRING_CHARS ];
	if ( gvm.GameClientConnect( reason, sizeof( reason ), clientNum, true, false ) )
	{
		Net::OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n[err_dialog]%s", reason );
		Log::Debug( "Game rejected a connection: %s.", reason );
		return;
	}

	SV_UserinfoChanged( new_client );

	// send the connect packet to the client
	Net::OutOfBandPrint( netsrc_t::NS_SERVER, from, "connectResponse" );

	Log::Debug( "Going from CS_FREE to CS_CONNECTED for %s", new_client->name );

	new_client->state = clientState_t::CS_CONNECTED;
	new_client->nextSnapshotTime = svs.time;
	new_client->lastPacketTime = svs.time;
	new_client->lastConnectTime = svs.time;

	// when we receive the first packet from the client, we will
	// notice that it is from a different serverid and that the
	// gamestate message was not just sent, forcing a retransmit
	new_client->gamestateMessageNum = -1;

	// if this was the first client on the server, or the last client
	// the server can hold, send a heartbeat to the master.
	int count = std::count_if(clients_begin, clients_end,
		[](const client_t& client) {
			return client.state >= clientState_t::CS_CONNECTED;
	});

	if ( count == 1 || count == sv_maxclients->integer )
	{
		SV_Heartbeat_f();
	}
}
Example #6
0
/*
 * ==================
 * SVC_DirectConnect
 *
 * 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;
    int      previousclients;                           // rich: connection limit per IP

    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));

    // r1ch: limit connections from a single IP
    previousclients = 0;
    for (i = 0, cl = svs.clients; i < (int)maxclients->value; i++, cl++)
    {
        if (cl->state == cs_free)
        {
            continue;
        }
        if (NET_CompareBaseAdr(adr, cl->netchan.remote_address))
        {
            // r1ch: zombies are less dangerous
            if (cl->state == cs_zombie)
            {
                previousclients++;
            }
            else
            {
                previousclients += 2;
            }
        }
    }
    if (previousclients >= (int)sv_iplimit->value * 2)
    {
        Netchan_OutOfBandPrint(NS_SERVER, adr, "print\nToo many connections from your host.\n");
        Com_DPrintf("    too many connections\n");
        return;
    }
    // end r1ch fix

    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_free)
        {
            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;
            }
            // r1ch: !! fix nasty bug where non-disconnected clients (from dropped disconnect
            // packets) could be overwritten!
            if (cl->state != cs_zombie)
            {
                Com_DPrintf("    client already found\n");
                // If we legitly get here, spoofed udp isn't possible (passed challenge) and client addr/port combo
                // is exactly the same, so we can assume its really a dropped/crashed client. i hope...
                Com_Printf("Dropping %s, ghost reconnect\n", cl->name);
                SV_DropClient(cl);
            }
            // end r1ch fix

            Com_Printf("%s:reconnect\n", NET_AdrToString(adr));

            SV_CleanClient(cl);                 // r1ch: clean up last client data

            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;
}
Example #7
0
/*
==================
SV_DirectConnect

A "connect" OOB command has been received
==================
*/
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;
	char		*ip;
#ifdef _XBOX
	bool		reconnect = false;
#endif

	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++) {

/* This was preventing sv_reconnectlimit from working.  It seems like commenting this
   out has solved the problem.  HOwever, if there is a future problem then it could
   be this.
   
		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)) {
				NET_OutOfBandPrint( NS_SERVER, from, "print\nReconnect rejected : too soon\n" );
				Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (from));
				return;
			}
			break;
		}
	}

	// don't let "ip" overflow userinfo string
	if ( NET_IsLocalAddress (from) )
		ip = "localhost";
	else
		ip = (char *)NET_AdrToString( from );
	if( ( strlen( ip ) + strlen( userinfo ) + 4 ) >= MAX_INFO_STRING ) {
		NET_OutOfBandPrint( NS_SERVER, from,
			"print\nUserinfo string length exceeded.  "
			"Try removing setu cvars from your config.\n" );
		return;
	}
	Info_SetValueForKey( userinfo, "ip", ip );

	// 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;
		}

		ping = svs.time - svs.challenges[i].pingTime;
		Com_Printf( SE_GetString("MP_SVGAME", "CLIENT_CONN_WITH_PING"), i, ping);//"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, va("print\n%s\n", SE_GetString("MP_SVGAME", "SERVER_FOR_HIGH_PING")));//Server is for high pings only\n" );
				Com_DPrintf (SE_GetString("MP_SVGAME", "CLIENT_REJECTED_LOW_PING"), i);//"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, va("print\n%s\n", SE_GetString("MP_SVGAME", "SERVER_FOR_LOW_PING")));//Server is for low pings only\n" );
				Com_DPrintf (SE_GetString("MP_SVGAME", "CLIENT_REJECTED_HIGH_PING"), i);//"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;
#ifdef _XBOX
			reconnect = true;
#endif
			// VVFIXME - both SOF2 and Wolf remove this call, claiming it blows away the user's info
			// 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");
				newcl = &svs.clients[sv_maxclients->integer - 1];
			}
			else {
				Com_Error( ERR_FATAL, "server is full on local connect\n" );
				return;
			}
		}
		else {
			const char *SV_GetStringEdString(char *refSection, char *refName);
			NET_OutOfBandPrint( NS_SERVER, from, va("print\n%s\n", SV_GetStringEdString("MP_SVGAME","SERVER_IS_FULL")));
			Com_DPrintf ("Rejected a connection.\n");
			return;
		}
	}

	// we got a newcl, so reset the reliableSequence and reliableAcknowledge
	cl->reliableAcknowledge = 0;
	cl->reliableSequence = 0;

gotnewcl:	

	// build a new connection
	// accept the new client
	// this is the only place a client_t is ever initialized
	*newcl = temp;
	clientNum = newcl - svs.clients;
	ent = SV_GentityNum( clientNum );
	newcl->gentity = ent;

	// save the challenge
	newcl->challenge = challenge;

	// save the address
	Netchan_Setup (NS_SERVER, &newcl->netchan , from, qport);

	// save the userinfo
	Q_strncpyz( newcl->userinfo, userinfo, sizeof(newcl->userinfo) );

	// get the game a chance to reject this connection or modify the userinfo
	denied = (char *)VM_Call( gvm, GAME_CLIENT_CONNECT, clientNum, qtrue, qfalse ); // firstTime = qtrue
	if ( denied ) {
		// we can't just use VM_ArgPtr, because that is only valid inside a VM_Call
		denied = (char *)VM_ExplicitArgPtr( gvm, (int)denied );

		NET_OutOfBandPrint( NS_SERVER, from, "print\n%s\n", denied );
		Com_DPrintf ("Game rejected a connection: %s.\n", denied);
		return;
	}

	SV_UserinfoChanged( newcl );

	// send the connect packet to the client
	NET_OutOfBandPrint( NS_SERVER, from, "connectResponse" );

	Com_DPrintf( "Going from CS_FREE to CS_CONNECTED for %s\n", newcl->name );

	newcl->state = CS_CONNECTED;
	newcl->nextSnapshotTime = svs.time;
	newcl->lastPacketTime = svs.time;
	newcl->lastConnectTime = svs.time;
	
	// when we receive the first packet from the client, we will
	// notice that it is from a different serverid and that the
	// gamestate message was not just sent, forcing a retransmit
	newcl->gamestateMessageNum = -1;

	newcl->lastUserInfoChange = 0; //reset the delay
	newcl->lastUserInfoCount = 0; //reset the count

	// if this was the first client on the server, or the last client
	// the server can hold, send a heartbeat to the master.
	count = 0;
	for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
		if ( svs.clients[i].state >= CS_CONNECTED ) {
			count++;
		}
	}
	if ( count == 1 || count == sv_maxclients->integer ) {
		SV_Heartbeat_f();
	}
}
void SV_DirectConnect( netadr_t from ) {
	char		userinfo[MAX_INFO_STRING];
	int			i;
	client_t	*cl, *newcl;
	client_t	temp;
	sharedEntity_t *ent;
	int			clientNum;
	int			qport;
	int			challenge;
	char		*password;
	int			startIndex;
	intptr_t		denied;
	int			count;

	Com_DPrintf ("SVC_DirectConnect ()\n");

	Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) );

	if ( Q_stricmp( Info_ValueForKey( userinfo, "protocol" ), PROTOCOL_VERSION ) ) {
		NET_OutOfBandPrint( NS_SERVER, from, "print\nServer uses protocol version "PROTOCOL_VERSION".\n" );
		Com_DPrintf ("    rejected connect from version %s\n", Info_ValueForKey( userinfo, "protocol" ));
		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 < MAX_PLAYERS ; 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");
				newcl = &svs.clients[sv_maxclients->integer - 1];
			}
			else {
				Com_Error( ERR_FATAL, "server is full on local connect\n" );
				return;
			}
		}
		else {
			NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is full.\n" );
			Com_DPrintf ("Rejected a connection.\n");
			return;
		}
	}

	// we got a newcl, so reset the reliableSequence and reliableAcknowledge
	cl->reliableAcknowledge = 0;
	cl->reliableSequence = 0;

gotnewcl:	
	// build a new connection
	// accept the new client
	// this is the only place a client_t is ever initialized
	*newcl = temp;
	clientNum = newcl - svs.clients;
	ent = SV_GentityNum( clientNum );
	newcl->gentity = ent;

	// save the challenge
	newcl->challenge = challenge;

	// save the address
	Netchan_Setup (NS_SERVER, &newcl->netchan , from, qport);
	// init the netchan queue
	newcl->netchan_end_queue = &newcl->netchan_start_queue;

	// save the userinfo
	Q_strncpyz( newcl->userinfo, userinfo, sizeof(newcl->userinfo) );

	// get the game a chance to reject this connection or modify the userinfo
	denied = VM_Call( gvm, GAME_CLIENT_CONNECT, clientNum, qtrue, qfalse ); // firstTime = qtrue
	if ( denied ) {
		// we can't just use VM_ArgPtr, because that is only valid inside a VM_Call
		char *str = VM_ExplicitArgPtr( gvm, denied );

		NET_OutOfBandPrint( NS_SERVER, from, "error\n%s\n", str );
		Com_DPrintf ("Game rejected a connection: %s.\n", str);
		return;
	}

	SV_UserinfoChanged( newcl );

	// send the connect packet to the client
	NET_OutOfBandPrint( NS_SERVER, from, "connectResponse" );

	Com_DPrintf( "Going from CS_FREE to CS_CONNECTED for %s\n", newcl->name );

	newcl->state = CS_CONNECTED;
	newcl->nextSnapshotTime = svs.time;
	newcl->lastPacketTime = svs.time;
	newcl->lastConnectTime = svs.time;
	
	// when we receive the first packet from the client, we will
	// notice that it is from a different serverid and that the
	// gamestate message was not just sent, forcing a retransmit
	newcl->gamestateMessageNum = -1;

	// if this was the first client on the server, or the last client
	// the server can hold, send a heartbeat to the master.
	count = 0;
	for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
		if ( svs.clients[i].state >= CS_CONNECTED ) {
			count++;
		}
	}

	if ( count == 1 || count == sv_maxclients->integer ) {
		SV_Heartbeat_f();
	}
}
Example #9
0
/*
* TV_Downstream_ClientConnect
*/
static bool TV_Downstream_ClientConnect( const socket_t *socket, const netadr_t *address, client_t *client,
											char *userinfo, int game_port, int challenge, bool tv_client )
{
	assert( socket );
	assert( address );
	assert( client );
	assert( userinfo );

	// it may actually happen that we reuse a client slot (same IP, same port),
	// which is "attached" to an active relay, so we need to notify the relay that client
	// isn't active anymore, otherwise it'll get confused after we set the client's state 
	// to CS_CONNECTING down below
	if( client->relay )
		TV_Relay_ClientDisconnect( client->relay, client );

	if( !TV_Lobby_CanConnect( client, userinfo ) )
		return false;

	TV_Lobby_ClientConnect( client );

	// the upstream is accepted, set up the client slot
	client->challenge = challenge; // save challenge for checksumming
	client->tv = (tv_client ? true : false);

	switch( socket->type )
	{
#ifdef TCP_ALLOW_TVCONNECT
	case SOCKET_TCP:
		client->reliable = true;
		client->individual_socket = true;
		client->socket = *socket;
		break;
#endif

	case SOCKET_UDP:
	case SOCKET_LOOPBACK:
		client->reliable = false;
		client->individual_socket = false;
		client->socket.open = false;
		break;

	default:
		assert( false );
	}

	TV_Downstream_ClientResetCommandBuffers( client, true );

	// reset timeouts
	client->lastPacketReceivedTime = tvs.realtime;
	client->lastconnect = tvs.realtime;

	// init the upstream
	client->state = CS_CONNECTING;

	if( client->individual_socket )
		Netchan_Setup( &client->netchan, &client->socket, address, game_port );
	else
		Netchan_Setup( &client->netchan, socket, address, game_port );

	// parse some info from the info strings
	Q_strncpyz( client->userinfo, userinfo, sizeof( client->userinfo ) );
	TV_Downstream_UserinfoChanged( client );

	Com_Printf( "%s" S_COLOR_WHITE " connected\n", client->name );

	return true;
}
Example #10
0
/*
==================
SV_DirectConnect

A "connect" OOB command has been received
==================
*/
void SV_DirectConnect( netadr_t from ) {
	char		userinfo[MAX_INFO_STRING];
	int			i;
	client_t	*cl, *newcl;
	MAC_STATIC client_t	temp;
	gentity_t		*ent;
	int			clientNum;
	int			version;
	int			qport;
	int			challenge;
	char		*denied;

	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;
	}

	qport = atoi( Info_ValueForKey( userinfo, "qport" ) );

	challenge = atoi( Info_ValueForKey( userinfo, "challenge" ) );

	// see if the challenge is valid (local clients don't need to challenge)
	if ( !NET_IsLocalAddress (from) ) {
		NET_OutOfBandPrint( NS_SERVER, from, "print\nNo challenge for address.\n" );
		return;
	} else {
		// force the "ip" info key to "localhost"
		Info_SetValueForKey( userinfo, "ip", "localhost" );
	}

	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 < 1 ; 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 (( sv.time - cl->lastConnectTime) 
				< (sv_reconnectlimit->integer * 1000))
			{
				Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (from));
				return;
			}
			Com_Printf ("%s:reconnect\n", NET_AdrToString (from));
			newcl = cl;
			goto gotnewcl;
		}
	}


	newcl = NULL;
	for ( i = 0; i < 1 ; i++ ) {
		cl = &svs.clients[i];
		if (cl->state == CS_FREE) {
			newcl = cl;
			break;
		}
	}

	if ( !newcl ) {
		NET_OutOfBandPrint( NS_SERVER, from, "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;
	clientNum = newcl - svs.clients;
	ent = SV_GentityNum( clientNum );
	newcl->gentity = ent;

	// save the address
	Netchan_Setup (NS_SERVER, &newcl->netchan , from, qport);

	// save the userinfo
	Q_strncpyz( newcl->userinfo, userinfo, sizeof(newcl->userinfo) );

	// get the game a chance to reject this connection or modify the userinfo
	denied = ge->ClientConnect( clientNum, qtrue, eSavedGameJustLoaded ); // firstTime = qtrue
	if ( denied ) {
		NET_OutOfBandPrint( NS_SERVER, from, "print\n%s\n", denied );
		Com_DPrintf ("Game rejected a connection: %s.\n", denied);
		return;
	}

	SV_UserinfoChanged( newcl );

	// send the connect packet to the client
	NET_OutOfBandPrint( NS_SERVER, from, "connectResponse" );

	newcl->state = CS_CONNECTED;
	newcl->nextSnapshotTime = sv.time;
	newcl->lastPacketTime = sv.time;
	newcl->lastConnectTime = sv.time;

	// when we receive the first packet from the client, we will
	// notice that it is from a different serverid and that the
	// gamestate message was not just sent, forcing a retransmit
	newcl->gamestateMessageNum = -1;
}
Example #11
0
/*
==================
SVC_DirectConnect

A connection request that did not come from the master
==================
*/
static void SVC_DirectConnect (void)
{
	char		userinfo[1024];
	static		int	userid;
	netadr_t	adr;
	int			i;
	client_t	*cl, *newcl;
	client_t	temp;
	edict_t		*ent;
	int			edictnum;
	const char		*s;
	int			clients, spectators;
	qboolean	spectator;

	q_strlcpy (userinfo, Cmd_Argv(2), sizeof(userinfo));

	// check for password or spectator_password
	s = Info_ValueForKey (userinfo, "spectator");
	if (s[0] && strcmp(s, "0"))
	{
		if (spectator_password.string[0] && 
			q_strcasecmp(spectator_password.string, "none") &&
			strcmp(spectator_password.string, s) )
		{	// failed
			Con_Printf ("%s:spectator password failed\n", NET_AdrToString (net_from));
			Netchan_OutOfBandPrint (net_from, "%c\nrequires a spectator password\n\n", A2C_PRINT);
			return;
		}
		Info_SetValueForStarKey (userinfo, "*spectator", "1", MAX_INFO_STRING);
		spectator = true;
		Info_RemoveKey (userinfo, "spectator"); // remove passwd
	}
	else
	{
		s = Info_ValueForKey (userinfo, "password");
		if (password.string[0] && 
			q_strcasecmp(password.string, "none") &&
			strcmp(password.string, s) )
		{
			Con_Printf ("%s:password failed\n", NET_AdrToString (net_from));
			Netchan_OutOfBandPrint (net_from, "%c\nserver requires a password\n\n", A2C_PRINT);
			return;
		}
		spectator = false;
		Info_RemoveKey (userinfo, "password"); // remove passwd
	}

	adr = net_from;
	userid++;	// so every client gets a unique id

	newcl = &temp;
	memset (newcl, 0, sizeof(client_t));

	newcl->userid = userid;
	newcl->portals = atoi(Cmd_Argv(1));

	// works properly
	if (!sv_highchars.integer)
	{
		byte	*p, *q;

		for (p = (byte *)newcl->userinfo, q = (byte *)userinfo;
			*q && p < (byte *)newcl->userinfo + sizeof(newcl->userinfo)-1; q++)
		{
			if (*q > 31 && *q <= 127)
				*p++ = *q;
		}
	}
	else
	{
		q_strlcpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo));
	}

	// if there is already a slot for this ip, drop it
	for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++)
	{
		if (cl->state == cs_free)
			continue;
		if (NET_CompareAdr (adr, cl->netchan.remote_address))
		{
			Con_Printf ("%s:reconnect\n", NET_AdrToString (adr));
			SV_DropClient (cl);
			break;
		}
	}

	// count up the clients and spectators
	clients = 0;
	spectators = 0;
	for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++)
	{
		if (cl->state == cs_free)
			continue;
		if (cl->spectator)
			spectators++;
		else
			clients++;
	}

	// if at server limits, refuse connection
	if (maxclients.integer > MAX_CLIENTS)
		Cvar_SetValue ("maxclients", MAX_CLIENTS);
	if (maxspectators.integer > MAX_CLIENTS)
		Cvar_SetValue ("maxspectators", MAX_CLIENTS);
	if (maxspectators.integer + maxclients.integer > MAX_CLIENTS)
		Cvar_SetValue ("maxspectators", MAX_CLIENTS - maxspectators.integer + maxclients.integer);
	if ( (spectator && spectators >= maxspectators.integer)
		|| (!spectator && clients >= maxclients.integer) )
	{
		Con_Printf ("%s:full connect\n", NET_AdrToString (adr));
		Netchan_OutOfBandPrint (adr, "%c\nserver is full\n\n", A2C_PRINT);
		return;
	}

	// find a client slot
	newcl = NULL;
	for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++)
	{
		if (cl->state == cs_free)
		{
			newcl = cl;
			break;
		}
	}
	if (!newcl)
	{
		Con_Printf ("WARNING: miscounted available clients\n");
		return;
	}

	// build a new connection
	// accept the new client
	// this is the only place a client_t is ever initialized
	*newcl = temp;

	Netchan_OutOfBandPrint (adr, "%c", S2C_CONNECTION );

	edictnum = (newcl-svs.clients)+1;

	Netchan_Setup (&newcl->netchan, adr);

	newcl->state = cs_connected;

	SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf));
	newcl->datagram.allowoverflow = true;

	// spectator mode can ONLY be set at join time
	newcl->spectator = spectator;

	ent = EDICT_NUM(edictnum);
	newcl->edict = ent;
	ED_ClearEdict (ent);

	// parse some info from the info strings
	SV_ExtractFromUserinfo (newcl);

	// JACK: Init the floodprot stuff.
	for (i = 0; i < 10; i++)
		newcl->whensaid[i] = 0.0;
	newcl->whensaidhead = 0;
	newcl->lockedtill = 0;

	// call the progs to get default spawn parms for the new client
	PR_ExecuteProgram (pr_global_struct->SetNewParms);
	for (i = 0; i < NUM_SPAWN_PARMS; i++)
		newcl->spawn_parms[i] = (&pr_global_struct->parm1)[i];

	if (newcl->spectator)
		Con_Printf ("Spectator %s connected\n", newcl->name);
	else
		Con_DPrintf ("Client %s connected\n", newcl->name);
}
Example #12
0
/*
==================
SV_DirectConnect

A "connect" OOB command has been received
==================
*/
void SV_DirectConnect( netadr_t from ) {
	char		userinfo[MAX_INFO_STRING];
	int			i;
	client_t	*cl, *newcl;
	client_t	temp;
	sharedEntity_t *ent;
	int			clientNum;
	int			version;
	int			qport;
	int			challenge;
	char		*password;
	int			startIndex;
	char		*denied;
	int			count;
	char		*ip;

	Com_DPrintf ("SVC_DirectConnect ()\n");

	// Check whether this client is banned.
	if ( SV_IsBanned( &from, qfalse ) )
	{
		NET_OutOfBandPrint( NS_SERVER, from, "print\nYou are banned from this server.\n" );
		Com_DPrintf( "    rejected connect from %s (banned)\n", NET_AdrToString(from) );
		return;
	}

	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 (yours is %i).\n", PROTOCOL_VERSION, 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++) {

/* This was preventing sv_reconnectlimit from working.  It seems like commenting this
   out has solved the problem.  HOwever, if there is a future problem then it could
   be this.

		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)) {
				NET_OutOfBandPrint( NS_SERVER, from, "print\nReconnect rejected : too soon\n" );
				Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (from));
				return;
			}
			break;
		}
	}

	// don't let "ip" overflow userinfo string
	if ( NET_IsLocalAddress (from) )
		ip = "localhost";
	else
		ip = (char *)NET_AdrToString( from );
	if( ( strlen( ip ) + strlen( userinfo ) + 4 ) >= MAX_INFO_STRING ) {
		NET_OutOfBandPrint( NS_SERVER, from,
			"print\nUserinfo string length exceeded.  "
			"Try removing setu cvars from your config.\n" );
		return;
	}
	Info_SetValueForKey( userinfo, "ip", ip );

	// see if the challenge is valid (localhost clients don't need to challenge)
	if (!NET_IsLocalAddress(from))
	{
		// Verify the received challenge against the expected challenge
		if (!SV_VerifyChallenge(challenge, from))
		{
			NET_OutOfBandPrint( NS_SERVER, from, "print\nIncorrect challenge for your address.\n" );
			return;
		}
	}

	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;
			// VVFIXME - both SOF2 and Wolf remove this call, claiming it blows away the user's info
			// disconnect the client from the game first so any flags the
			// player might have are dropped
			GVM_ClientDisconnect( 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");
				newcl = &svs.clients[sv_maxclients->integer - 1];
			}
			else {
				Com_Error( ERR_FATAL, "server is full on local connect\n" );
				return;
			}
		}
		else {
			const char *SV_GetStringEdString(char *refSection, char *refName);
			NET_OutOfBandPrint( NS_SERVER, from, va("print\n%s\n", SV_GetStringEdString("MP_SVGAME","SERVER_IS_FULL")));
			Com_DPrintf ("Rejected a connection.\n");
			return;
		}
	}

	// we got a newcl, so reset the reliableSequence and reliableAcknowledge
	cl->reliableAcknowledge = 0;
	cl->reliableSequence = 0;

gotnewcl:

	// build a new connection
	// accept the new client
	// this is the only place a client_t is ever initialized
	*newcl = temp;
	clientNum = newcl - svs.clients;
	ent = SV_GentityNum( clientNum );
	newcl->gentity = ent;

	// save the challenge
	newcl->challenge = challenge;

	// save the address
	Netchan_Setup (NS_SERVER, &newcl->netchan , from, qport);

	// save the userinfo
	Q_strncpyz( newcl->userinfo, userinfo, sizeof(newcl->userinfo) );

	// get the game a chance to reject this connection or modify the userinfo
	denied = GVM_ClientConnect( clientNum, qtrue, qfalse ); // firstTime = qtrue
	if ( denied ) {
		NET_OutOfBandPrint( NS_SERVER, from, "print\n%s\n", denied );
		Com_DPrintf ("Game rejected a connection: %s.\n", denied);
		return;
	}

	SV_UserinfoChanged( newcl );

	// send the connect packet to the client
	NET_OutOfBandPrint( NS_SERVER, from, "connectResponse" );

	Com_DPrintf( "Going from CS_FREE to CS_CONNECTED for %s\n", newcl->name );

	newcl->state = CS_CONNECTED;
	newcl->nextSnapshotTime = svs.time;
	newcl->lastPacketTime = svs.time;
	newcl->lastConnectTime = svs.time;

	// when we receive the first packet from the client, we will
	// notice that it is from a different serverid and that the
	// gamestate message was not just sent, forcing a retransmit
	newcl->gamestateMessageNum = -1;

	newcl->lastUserInfoChange = 0; //reset the delay
	newcl->lastUserInfoCount = 0; //reset the count

	// if this was the first client on the server, or the last client
	// the server can hold, send a heartbeat to the master.
	count = 0;
	for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
		if ( svs.clients[i].state >= CS_CONNECTED ) {
			count++;
		}
	}
	if ( count == 1 || count == sv_maxclients->integer ) {
		SV_Heartbeat_f();
	}
}
Example #13
0
/*
====================
CL_PlayDemo_f

playdemo <demoname>
====================
*/
void CL_PlayDemo_f( void )
{
	string	filename;
	string	demoname;
	int	i;

	if( Cmd_Argc() != 2 )
	{
		Msg( "Usage: playdemo <demoname>\n" );
		return;
	}

	if( cls.demoplayback )
	{
		CL_StopPlayback();
	}

	if( cls.demorecording )
	{
		Msg( "Can't playback during demo record.\n");
		return;
	}

	Q_strncpy( demoname, Cmd_Argv( 1 ), sizeof( demoname ) - 1 );
	Q_snprintf( filename, sizeof( filename ), "demos/%s.dem", demoname );

	if( !FS_FileExists( filename, true ))
	{
		MsgDev( D_ERROR, "couldn't open %s\n", filename );
		cls.demonum = -1; // stop demo loop
		return;
	}

	cls.demofile = FS_Open( filename, "rb", true );
	Q_strncpy( cls.demoname, demoname, sizeof( cls.demoname ));
	Q_strncpy( menu.globals->demoname, demoname, sizeof( menu.globals->demoname ));

	// read in the m_DemoHeader
	FS_Read( cls.demofile, &demo.header, sizeof( demoheader_t ));

	if( demo.header.id != IDEMOHEADER )
	{
		MsgDev( D_ERROR, "%s is not a demo file\n", filename );
		FS_Close( cls.demofile );
		cls.demofile = NULL;
		cls.demonum = -1; // stop demo loop
		return;
	}

	if( demo.header.net_protocol != PROTOCOL_VERSION || demo.header.dem_protocol != DEMO_PROTOCOL )
	{
		MsgDev( D_ERROR, "demo protocol outdated\n"
			"Demo file protocols Network(%i), Demo(%i)\n"
			"Server protocol is at Network(%i), Demo(%i)\n",
			demo.header.net_protocol, 
			demo.header.dem_protocol,
			PROTOCOL_VERSION,
			DEMO_PROTOCOL
		);

		FS_Close( cls.demofile );
		cls.demofile = NULL;
		cls.demonum = -1; // stop demo loop
		return;
	}

	// now read in the directory structure.
	FS_Seek( cls.demofile, demo.header.directory_offset, SEEK_SET );
	FS_Read( cls.demofile, &demo.directory.numentries, sizeof( int ));

	if( demo.directory.numentries < 1 || demo.directory.numentries > 1024 )
	{
		MsgDev( D_ERROR, "demo had bogus # of directory entries: %i\n", demo.directory.numentries );
		FS_Close( cls.demofile );
		cls.demofile = NULL;
		cls.demonum = -1; // stop demo loop
		cls.changedemo = false;
		return;
	}

	if( cls.changedemo )
	{
		S_StopAllSounds();
		SCR_BeginLoadingPlaque( false );

		CL_ClearState ();
		CL_InitEdicts (); // re-arrange edicts
	}
	else
	{
		// NOTE: at this point demo is still valid
		CL_Disconnect();
		Host_ShutdownServer();

		Con_Close();
		UI_SetActiveMenu( false );
	}

	// allocate demo entries
	demo.directory.entries = Mem_Alloc( cls.mempool, sizeof( demoentry_t ) * demo.directory.numentries );

	for( i = 0; i < demo.directory.numentries; i++ )
	{
		FS_Read( cls.demofile, &demo.directory.entries[i], sizeof( demoentry_t ));
	}

	demo.entryIndex = 0;
	demo.entry = &demo.directory.entries[demo.entryIndex];

	FS_Seek( cls.demofile, demo.entry->offset, SEEK_SET );

	cls.demoplayback = true;
	cls.state = ca_connected;
	cl.background = (cls.demonum != -1) ? true : false;

	demo.starttime = CL_GetDemoPlaybackClock(); // for determining whether to read another message

	Netchan_Setup( NS_CLIENT, &cls.netchan, net_from, net_qport->integer );

	demo.framecount = 0;
	cls.lastoutgoingcommand = -1;
 	cls.nextcmdtime = host.realtime;

	// g-cont. is this need?
	Q_strncpy( cls.servername, demoname, sizeof( cls.servername ));

	// begin a playback demo
}
Example #14
0
/*
====================
CL_PlayDemo_f

play [demoname]
====================
*/
void CL_PlayDemo_f (void)
{
	char	name[256];
	qbool	try_dem;

	if (Cmd_Argc() != 2)
	{
		Com_Printf ("playdemo <demoname> : plays a demo\n");
		return;
	}

//
// disconnect from server
//
	Host_EndGame ();
	
//
// open the demo file
//
	strlcpy (name, Cmd_Argv(1), sizeof(name)-4);

#ifdef _WIN32
	if (strlen(name) > 4 && !Q_stricmp(name + strlen(name) - 4, ".qwz")) {
		PlayQWZDemo ();
		return;
	}
#endif

	if (COM_FileExtension(name)[0] == 0) {
		COM_DefaultExtension (name, ".qwd");
		try_dem = true;		// if we can't open the .qwd, try .dem also
	}
	else
		try_dem = false;

try_again:
	if (!strncmp(name, "../", 3) || !strncmp(name, "..\\", 3))
		cls.demofile = fopen (va("%s/%s", com_basedir, name+3), "rb");
	else
		FS_FOpenFile (name, &cls.demofile);

	if (!cls.demofile && try_dem) {
		COM_StripExtension (name, name);
		strcat (name, ".dem");
		try_dem = false;
		goto try_again;
	}

	if (!cls.demofile) {
		Com_Printf ("ERROR: couldn't open \"%s\"\n", Cmd_Argv(1));
		cls.playdemos = 0;
		return;
	}

	Com_Printf ("Playing demo from %s.\n", COM_SkipPath(name));

	cls.nqdemoplayback = !Q_stricmp(COM_FileExtension(name), "dem");
	if (cls.nqdemoplayback) {
		NQD_StartPlayback ();
	}

#ifdef MVDPLAY
	cls.mvdplayback = !Q_stricmp(COM_FileExtension(name), "mvd");
#endif

	cls.demoplayback = true;
	cls.state = ca_demostart;
	Netchan_Setup (NS_CLIENT, &cls.netchan, net_null, 0);
	cls.demotime = 0;

#ifdef MVDPLAY
	cls.mvd_newtime = cls.mvd_oldtime = 0;
	cls.mvd_findtarget = true;
	cls.mvd_lasttype = 0;
	cls.mvd_lastto = 0;
	MVD_ClearPredict();
#endif
}
Example #15
0
void PlayQWZDemo (void)
{
	extern cvar_t	qizmo_dir;
	STARTUPINFO			si;
	PROCESS_INFORMATION	pi;
	char	*name;
	char	qwz_name[256];
	char	cmdline[512];
	char	*p;

	if (hQizmoProcess) {
		Com_Printf ("Cannot unpack -- Qizmo still running!\n");
		return;
	}
	
	name = Cmd_Argv(1);

	if (!strncmp(name, "../", 3) || !strncmp(name, "..\\", 3))
		strlcpy (qwz_name, va("%s/%s", com_basedir, name+3), sizeof(qwz_name));
	else
		if (name[0] == '/' || name[0] == '\\')
			strlcpy (qwz_name, va("%s/%s", cls.gamedir, name+1), sizeof(qwz_name));
		else
			strlcpy (qwz_name, va("%s/%s", cls.gamedir, name), sizeof(qwz_name));

	// Qizmo needs an absolute file name
	_fullpath (qwz_name, qwz_name, sizeof(qwz_name)-1);
	qwz_name[sizeof(qwz_name)-1] = 0;

	// check if the file exists
	cls.demofile = fopen (qwz_name, "rb");
	if (!cls.demofile)
	{
		Com_Printf ("Couldn't open %s\n", name);
		return;
	}
	fclose (cls.demofile);
	
	strlcpy (tempqwd_name, qwz_name, sizeof(tempqwd_name)-4);
#if 0
	// the right way
	strcpy (tempqwd_name + strlen(tempqwd_name) - 4, ".qwd");
#else
	// the way Qizmo does it, sigh
	p = strstr (tempqwd_name, ".qwz");
	if (!p)
		p = strstr (tempqwd_name, ".QWZ");
	if (!p)
		p = tempqwd_name + strlen(tempqwd_name);
	strcpy (p, ".qwd");
#endif

	cls.demofile = fopen (tempqwd_name, "rb");
	if (cls.demofile) {
		// .qwd already exists, so just play it
		cls.demoplayback = true;
		cls.state = ca_demostart;
		Netchan_Setup (NS_CLIENT, &cls.netchan, net_null, 0);
		cls.demotime = 0;
		return;
	}
	
	Com_Printf ("Unpacking %s...\n", COM_SkipPath(name));
	
	// start Qizmo to unpack the demo
	memset (&si, 0, sizeof(si));
	si.cb = sizeof(si);
	si.wShowWindow = SW_HIDE;
	si.dwFlags = STARTF_USESHOWWINDOW;
	
	strlcpy (cmdline, va("%s/%s/qizmo.exe -q -u -D \"%s\"", com_basedir,
		qizmo_dir.string, qwz_name), sizeof(cmdline));
	
	if (!CreateProcess (NULL, cmdline, NULL, NULL,
		FALSE, 0/* | HIGH_PRIORITY_CLASS*/,
		NULL, va("%s/%s", com_basedir, qizmo_dir.string), &si, &pi))
	{
		Com_Printf ("Couldn't execute %s/%s/qizmo.exe\n",
			com_basedir, qizmo_dir.string);
		return;
	}
	
	hQizmoProcess = pi.hProcess;
	qwz_unpacking = true;
	qwz_playback = true;

	// demo playback doesn't actually start yet, we just set
	// cls.demoplayback so that CL_StopPlayback() will be called
	// if CL_Disconnect() is issued
	cls.demoplayback = true;
	cls.demofile = NULL;
	cls.state = ca_demostart;
	Netchan_Setup (NS_CLIENT, &cls.netchan, net_null, 0);
	cls.demotime = 0;
}
Example #16
0
void SV_DirectConnect( netadr_t from ) {
	char		userinfo[MAX_INFO_STRING];
	int			i;
	client_t	*cl, *newcl;
	client_t	temp;
	sharedEntity_t *ent;
	int			clientNum;
	int			version;
	int			qport;
	int			challenge;
	char		*password;
	int			startIndex;
	intptr_t		denied;
	int			count;
	char		*ip;
	const char	*stringEd;
#ifdef LEGACY_PROTOCOL
	qboolean	compat = qfalse;
#endif

	Com_DPrintf ("SVC_DirectConnect ()\n");
	
	// Check whether this client is banned.
	if(SV_IsBanned(&from, qfalse))
	{
		NET_OutOfBandPrint(NS_SERVER, from, "print\nYou are banned from this server.\n");
		return;
	}

	Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) );

	version = atoi(Info_ValueForKey(userinfo, "protocol"));
	
#ifdef LEGACY_PROTOCOL
	if(version > 0 && com_legacyprotocol->integer == version)
		compat = qtrue;
	else
#endif
	{
		if(version != com_protocol->integer)
		{
			NET_OutOfBandPrint(NS_SERVER, from, "print\nServer uses protocol version %i "
					   "(yours is %i).\n", com_protocol->integer, 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;
		}
	}
	
	// don't let "ip" overflow userinfo string
	if ( NET_IsLocalAddress (from) )
		ip = "localhost";
	else
		ip = (char *)NET_AdrToString( from );
	if( ( strlen( ip ) + strlen( userinfo ) + 4 ) >= MAX_INFO_STRING ) {
		NET_OutOfBandPrint( NS_SERVER, from,
			"print\nUserinfo string length exceeded.  "
			"Try removing setu cvars from your config.\n" );
		return;
	}
	Info_SetValueForKey( userinfo, "ip", ip );

	// see if the challenge is valid (LAN clients don't need to challenge)
	if (!NET_IsLocalAddress(from))
	{
		int ping;
		challenge_t *challengeptr;

		for (i=0; i<MAX_CHALLENGES; i++)
		{
			if (NET_CompareAdr(from, svs.challenges[i].adr))
			{
				if(challenge == svs.challenges[i].challenge)
					break;
			}
		}

		if (i == MAX_CHALLENGES)
		{
			NET_OutOfBandPrint( NS_SERVER, from, "print\nNo or bad challenge for your address.\n" );
			return;
		}
	
		challengeptr = &svs.challenges[i];
		
		if(challengeptr->wasrefused)
		{
			// Return silently, so that error messages written by the server keep being displayed.
			return;
		}

		ping = svs.time - challengeptr->pingTime;

		// never reject a LAN client based on ping
		if ( !Sys_IsLANAddress( from ) ) {
			if ( sv_minPing->value && ping < sv_minPing->value ) {
				NET_OutOfBandPrint( NS_SERVER, from, "print\n%s", SV_StringEdString("SERVER_FOR_HIGH_PING") );
				stringEd = SV_GetString("CLIENT_REJECTED_LOW_PING");
				Com_DPrintf (stringEd, i);
				challengeptr->wasrefused = qtrue;
				return;
			}
			if ( sv_maxPing->value && ping > sv_maxPing->value ) {
				NET_OutOfBandPrint( NS_SERVER, from, "print\n%s", SV_StringEdString("SERVER_FOR_LOW_PING") );
				stringEd = SV_GetString("CLIENT_REJECTED_LOW_PING");
				Com_DPrintf (stringEd, i);
				challengeptr->wasrefused = qtrue;
				return;
			}
		}

		stringEd = SV_GetString("CLIENT_CONN_WITH_PING");
		Com_Printf(stringEd, i, ping);
		challengeptr->connected = qtrue;
	}

	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");
				newcl = &svs.clients[sv_maxclients->integer - 1];
			}
			else {
				Com_Error( ERR_FATAL, "server is full on local connect" );
				return;
			}
		}
		else {
			NET_OutOfBandPrint( NS_SERVER, from, "print\n%s\n", SV_StringEdString("SERVER_IS_FULL") );
			Com_DPrintf ("Rejected a connection.\n");
			return;
		}
	}

	// we got a newcl, so reset the reliableSequence and reliableAcknowledge
	cl->reliableAcknowledge = 0;
	cl->reliableSequence = 0;

gotnewcl:	
	// build a new connection
	// accept the new client
	// this is the only place a client_t is ever initialized
	*newcl = temp;
	clientNum = newcl - svs.clients;
	ent = SV_GentityNum( clientNum );
	newcl->gentity = ent;

	// save the challenge
	newcl->challenge = challenge;

	// save the address
#ifdef LEGACY_PROTOCOL
	newcl->compat = compat;
	Netchan_Setup(NS_SERVER, &newcl->netchan, from, qport, challenge, compat);
#else
	Netchan_Setup(NS_SERVER, &newcl->netchan, from, qport, challenge, qfalse);
#endif
	// init the netchan queue
	newcl->netchan_end_queue = &newcl->netchan_start_queue;

	// save the userinfo
	Q_strncpyz( newcl->userinfo, userinfo, sizeof(newcl->userinfo) );

	// get the game a chance to reject this connection or modify the userinfo
	denied = VM_Call( gvm, GAME_CLIENT_CONNECT, clientNum, qtrue, qfalse ); // firstTime = qtrue
	if ( denied ) {
		// we can't just use VM_ArgPtr, because that is only valid inside a VM_Call
		char *str = VM_ExplicitArgPtr( gvm, denied );

		NET_OutOfBandPrint( NS_SERVER, from, "print\n%s\n", str );
		Com_DPrintf ("Game rejected a connection: %s.\n", str);
		return;
	}

	SV_UserinfoChanged( newcl );

	// send the connect packet to the client
	NET_OutOfBandPrint(NS_SERVER, from, "connectResponse %d", challenge);

	Com_DPrintf( "Going from CS_FREE to CS_CONNECTED for %s\n", newcl->name );

	newcl->state = CS_CONNECTED;
	newcl->lastSnapshotTime = 0;
	newcl->lastPacketTime = svs.time;
	newcl->lastConnectTime = svs.time;
	
	// when we receive the first packet from the client, we will
	// notice that it is from a different serverid and that the
	// gamestate message was not just sent, forcing a retransmit
	newcl->gamestateMessageNum = -1;

	// if this was the first client on the server, or the last client
	// the server can hold, send a heartbeat to the master.
	count = 0;
	for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
		if ( svs.clients[i].state >= CS_CONNECTED ) {
			count++;
		}
	}
	if ( count == 1 || count == sv_maxclients->integer ) {
		SV_Heartbeat_f();
	}
}
Example #17
0
/*
=================
CL_ConnectionlessPacket

Responses to broadcasts, etc
=================
*/
void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
{
	char	*args;
	char	*c, buf[MAX_SYSPATH];
	int	len = sizeof( buf );
	int	dataoffset = 0;
	netadr_t	servadr;
	
	BF_Clear( msg );
	BF_ReadLong( msg ); // skip the -1

	args = BF_ReadStringLine( msg );

	Cmd_TokenizeString( args );
	c = Cmd_Argv( 0 );

	MsgDev( D_NOTE, "CL_ConnectionlessPacket: %s : %s\n", NET_AdrToString( from ), c );

	// server connection
	if( !Q_strcmp( c, "client_connect" ))
	{
		if( cls.state == ca_connected )
		{
			MsgDev( D_INFO, "dup connect received. ignored\n");
			return;
		}

		Netchan_Setup( NS_CLIENT, &cls.netchan, from, Cvar_VariableValue( "net_qport" ));
		BF_WriteByte( &cls.netchan.message, clc_stringcmd );
		BF_WriteString( &cls.netchan.message, "new" );
		cls.state = ca_connected;

		cl.validsequence = 0;		// haven't gotten a valid frame update yet
		cl.delta_sequence = -1;		// we'll request a full delta from the baseline
		cls.lastoutgoingcommand = -1;		// we don't have a backed up cmd history yet
		cls.nextcmdtime = host.realtime;	// we can send a cmd right away

		CL_StartupDemoHeader ();

		UI_SetActiveMenu( false );
	}
	else if( !Q_strcmp( c, "info" ))
	{
		// server responding to a status broadcast
		CL_ParseStatusMessage( from, msg );
	}
	else if( !Q_strcmp( c, "netinfo" ))
	{
		// server responding to a status broadcast
		CL_ParseNETInfoMessage( from, msg );
	}
	else if( !Q_strcmp( c, "cmd" ))
	{
		// remote command from gui front end
		if( !NET_IsLocalAddress( from ))
		{
			Msg( "Command packet from remote host. Ignored.\n" );
			return;
		}

		ShowWindow( host.hWnd, SW_RESTORE );
		SetForegroundWindow ( host.hWnd );
		args = BF_ReadString( msg );
		Cbuf_AddText( args );
		Cbuf_AddText( "\n" );
	}
	else if( !Q_strcmp( c, "print" ))
	{
		// print command from somewhere
		args = BF_ReadString( msg );
		Msg( args );
	}
	else if( !Q_strcmp( c, "ping" ))
	{
		// ping from somewhere
		Netchan_OutOfBandPrint( NS_CLIENT, from, "ack" );
	}
	else if( !Q_strcmp( c, "challenge" ))
	{
		// challenge from the server we are connecting to
		cls.challenge = Q_atoi( Cmd_Argv( 1 ));
		CL_SendConnectPacket();
		return;
	}
	else if( !Q_strcmp( c, "echo" ))
	{
		// echo request from server
		Netchan_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv( 1 ));
	}
	else if( !Q_strcmp( c, "disconnect" ))
	{
		// a disconnect message from the server, which will happen if the server
		// dropped the connection but it is still getting packets from us
		CL_Disconnect();
	}
	else if( clgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len ))
	{
		// user out of band message (must be handled in CL_ConnectionlessPacket)
		if( len > 0 ) Netchan_OutOfBand( NS_SERVER, from, len, buf );
	}
	else if( msg->pData[0] == 0xFF && msg->pData[1] == 0xFF && msg->pData[2] == 0xFF && msg->pData[3] == 0xFF && msg->pData[4] == 0x66 && msg->pData[5] == 0x0A )
	{
		dataoffset = 6;

		while( 1 )
		{
			servadr.type = NA_IP;
			servadr.ip[0] = msg->pData[dataoffset + 0];
			servadr.ip[1] = msg->pData[dataoffset + 1];
			servadr.ip[2] = msg->pData[dataoffset + 2];
			servadr.ip[3] = msg->pData[dataoffset + 3];

			servadr.port = *(word *)&msg->pData[dataoffset + 4];

			if( !servadr.port )
				break;

			MsgDev( D_INFO, "Found server: %s\n", NET_AdrToString( servadr ));

			NET_Config( true ); // allow remote

			Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION );

			dataoffset += 6;
		}
	}
	else MsgDev( D_ERROR, "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args );
}
Example #18
0
/*
* SV_ClientConnect
* accept the new client
* this is the only place a client_t is ever initialized
*/
qboolean SV_ClientConnect( const socket_t *socket, const netadr_t *address, client_t *client, char *userinfo,
						  int game_port, int challenge, qboolean fakeClient, qboolean tvClient,
						  unsigned int ticket_id, int session_id )
{
	int i;
	edict_t	*ent;
	int edictnum;

	edictnum = ( client - svs.clients ) + 1;
	ent = EDICT_NUM( edictnum );

	// give mm a chance to reject if the server is locked ready for mm
	// must be called before ge->ClientConnect
	// ch : rly ignore fakeClient and tvClient here?
	session_id = SV_MM_ClientConnect( address, userinfo, ticket_id, session_id );
	if( !session_id )
		return qfalse;

	// we need to set local sessions to userinfo ourselves
	if( session_id < 0 )
		Info_SetValueForKey( userinfo, "cl_mm_session", va("%d", session_id) );

	// get the game a chance to reject this connection or modify the userinfo
	if( !ge->ClientConnect( ent, userinfo, fakeClient, tvClient ) )
		return qfalse;


	// the connection is accepted, set up the client slot
	memset( client, 0, sizeof( *client ) );
	client->edict = ent;
	client->challenge = challenge; // save challenge for checksumming

	client->tvclient = tvClient;

	client->mm_session = session_id;
	client->mm_ticket = ticket_id;

	if( socket )
	{
		switch( socket->type )
		{
#ifdef TCP_ALLOW_CONNECT
		case SOCKET_TCP:
			client->reliable = qtrue;
			client->individual_socket = qtrue;
			client->socket = *socket;
			break;
#endif

		case SOCKET_UDP:
		case SOCKET_LOOPBACK:
			client->reliable = qfalse;
			client->individual_socket = qfalse;
			client->socket.open = qfalse;
			break;

		default:
			assert( qfalse );
		}
	}
	else
	{
		assert( fakeClient );
		client->reliable = qfalse;
		client->individual_socket = qfalse;
		client->socket.open = qfalse;
	}

	SV_ClientResetCommandBuffers( client );

	// reset timeouts
	client->lastPacketReceivedTime = svs.realtime;
	client->lastconnect = svs.realtime;

	// init the connection
	client->state = CS_CONNECTING;

	if( fakeClient )
	{
		client->netchan.remoteAddress.type = NA_NOTRANSMIT; // fake-clients can't transmit
		// TODO: if mm_debug_reportbots
		Info_SetValueForKey( userinfo, "cl_mm_session", va("%d", client->mm_session) );
	}
	else
	{
		if( client->individual_socket )
		{
			Netchan_Setup( &client->netchan, &client->socket, address, game_port );
		}
		else
		{
			Netchan_Setup( &client->netchan, socket, address, game_port );
		}
	}

	
	// create default rating for the client and current gametype
	ge->AddDefaultRating( ent, NULL );

	// parse some info from the info strings
	Q_strncpyz( client->userinfo, userinfo, sizeof( client->userinfo ) );
	SV_UserinfoChanged( client );

	// generate session id
	for( i = 0; i < sizeof( svs.clients[0].session ) - 1; i++ ) {
		 const unsigned char symbols[65] =
			"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
		client->session[i] = symbols[rand() % (sizeof( symbols )-1)];
	}
	client->session[i] = '\0';

	return qtrue;
}
Example #19
0
/*
=================
CL_ConnectionlessPacket

Responses to broadcasts, etc
=================
*/
void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
	char	*s;
	char	*c;
	
	MSG_BeginReading( msg );
	MSG_ReadLong( msg );	// skip the -1

	s = MSG_ReadStringLine( msg );

	Cmd_TokenizeString( s );

	c = Cmd_Argv(0);

	Com_DPrintf ("CL packet %s: %s\n", NET_AdrToString(from), c);

	// challenge from the server we are connecting to
	if ( !strcmp(c, "challengeResponse") ) {
		if ( cls.state != CA_CONNECTING ) {
			Com_Printf( "Unwanted challenge response received.  Ignored.\n" );
		} else {
			// start sending challenge repsonse instead of challenge request packets
			clc.challenge = atoi(Cmd_Argv(1));
			cls.state = CA_CHALLENGING;
			clc.connectPacketCount = 0;
			clc.connectTime = -99999;

			// take this address as the new server address.  This allows
			// a server proxy to hand off connections to multiple servers
			clc.serverAddress = from;
		}
		return;
	}

	// server connection
	if ( !strcmp(c, "connectResponse") ) {
		if ( cls.state >= CA_CONNECTED ) {
			Com_Printf ("Dup connect received.  Ignored.\n");
			return;
		}
		if ( cls.state != CA_CHALLENGING ) {
			Com_Printf ("connectResponse packet while not connecting.  Ignored.\n");
			return;
		}
		if ( !NET_CompareBaseAdr( from, clc.serverAddress ) ) {
			Com_Printf( "connectResponse from a different address.  Ignored.\n" );
			Com_Printf( "%s should have been %s\n", NET_AdrToString( from ), 
				NET_AdrToString( clc.serverAddress ) );
			return;
		}
		Netchan_Setup (NS_CLIENT, &clc.netchan, from, Cvar_VariableIntegerValue( "qport" ) );
		cls.state = CA_CONNECTED;
		clc.lastPacketSentTime = -9999;		// send first packet immediately
		return;
	}

	// a disconnect message from the server, which will happen if the server
	// dropped the connection but it is still getting packets from us
	if (!strcmp(c, "disconnect")) {
		CL_DisconnectPacket( from );
		return;
	}

	// echo request from server
	if ( !strcmp(c, "echo") ) {
		NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) );
		return;
	}

	// print request from server
	if ( !strcmp(c, "print") ) {
		s = MSG_ReadString( msg );
		UI_UpdateConnectionMessageString( s );
		Com_Printf( "%s", s );
		return;
	}


	Com_DPrintf ("Unknown connectionless packet command.\n");
}
Example #20
0
/*
 * @brief A connection request that did not come from the master.
 */
static void Svc_Connect(void) {
	char user_info[MAX_USER_INFO_STRING];
	sv_client_t *cl, *client;
	int32_t i;

	Com_Debug("Svc_Connect()\n");

	net_addr_t *addr = &net_from;

	const int32_t version = strtol(Cmd_Argv(1), NULL, 0);

	// resolve protocol
	if (version != PROTOCOL) {
		Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nServer is version %d.\n", PROTOCOL);
		return;
	}

	const uint8_t qport = strtoul(Cmd_Argv(2), NULL, 0);

	const uint32_t challenge = strtoul(Cmd_Argv(3), NULL, 0);

	// copy user_info, leave room for ip stuffing
	g_strlcpy(user_info, Cmd_Argv(4), sizeof(user_info) - 25);

	if (*user_info == '\0') { // catch empty user_info
		Com_Print("Empty user_info from %s\n", Net_NetaddrToString(addr));
		Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n");
		return;
	}

	if (strchr(user_info, '\xFF')) { // catch end of message in string exploit
		Com_Print("Illegal user_info contained xFF from %s\n", Net_NetaddrToString(addr));
		Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n");
		return;
	}

	if (strlen(GetUserInfo(user_info, "ip"))) { // catch spoofed ips
		Com_Print("Illegal user_info contained ip from %s\n", Net_NetaddrToString(addr));
		Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n");
		return;
	}

	if (!ValidateUserInfo(user_info)) { // catch otherwise invalid user_info
		Com_Print("Invalid user_info from %s\n", Net_NetaddrToString(addr));
		Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n");
		return;
	}

	// force the ip so the game can filter on it
	SetUserInfo(user_info, "ip", Net_NetaddrToString(addr));

	// enforce a valid challenge to avoid denial of service attack
	for (i = 0; i < MAX_CHALLENGES; i++) {
		if (Net_CompareClientNetaddr(addr, &svs.challenges[i].addr)) {
			if (challenge == svs.challenges[i].challenge) {
				svs.challenges[i].challenge = 0;
				break; // good
			}
			Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nBad challenge\n");
			return;
		}
	}
	if (i == MAX_CHALLENGES) {
		Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nNo challenge for address\n");
		return;
	}

	// resolve the client slot
	client = NULL;

	// first check for an ungraceful reconnect (client crashed, perhaps)
	for (i = 0, cl = svs.clients; i < sv_max_clients->integer; i++, cl++) {

		const net_chan_t *ch = &cl->net_chan;

		if (cl->state == SV_CLIENT_FREE) // not in use, not interested
			continue;

		// the base address and either the qport or real port must match
		if (Net_CompareClientNetaddr(addr, &ch->remote_address)) {

			if (addr->port == ch->remote_address.port || qport == ch->qport) {
				client = cl;
				break;
			}
		}
	}

	// otherwise, treat as a fresh connect to a new slot
	if (!client) {
		for (i = 0, cl = svs.clients; i < sv_max_clients->integer; i++, cl++) {
			if (cl->state == SV_CLIENT_FREE && !cl->edict->ai) { // we have a free one
				client = cl;
				break;
			}
		}
	}

	// no soup for you, next!!
	if (!client) {
		Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nServer is full\n");
		Com_Debug("Rejected a connection\n");
		return;
	}

	// give the game a chance to reject this connection or modify the user_info
	if (!(svs.game->ClientConnect(client->edict, user_info))) {
		const char *rejmsg = GetUserInfo(user_info, "rejmsg");

		if (strlen(rejmsg)) {
			Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\n%s\nConnection refused\n", rejmsg);
		} else {
			Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "print\nConnection refused\n");
		}

		Com_Debug("Game rejected a connection\n");
		return;
	}

	// parse some info from the info strings
	g_strlcpy(client->user_info, user_info, sizeof(client->user_info));
	Sv_UserInfoChanged(client);

	// send the connect packet to the client
	Netchan_OutOfBandPrint(NS_UDP_SERVER, addr, "client_connect %s", sv_download_url->string);

	Netchan_Setup(NS_UDP_SERVER, &client->net_chan, addr, qport);

	Mem_InitBuffer(&client->datagram.buffer, client->datagram.data, sizeof(client->datagram.data));
	client->datagram.buffer.allow_overflow = true;

	client->last_message = svs.real_time; // don't timeout

	client->state = SV_CLIENT_CONNECTED;
}
Example #21
0
/*
==================
SVC_DirectConnect

A connection request that did not come from the master
==================
*/
void SVC_DirectConnect (void)
{
	char		userinfo[1024];
	netadr_t	adr;
	int			i;
	client_t	*cl, *newcl;
	edict_t		*ent;
	int			edictnum;
	char		*s;
	int			clients, spectators;
	qbool		spectator;
	int			qport;
	int			version;
	int			challenge;

	version = atoi(Cmd_Argv(1));
	if (version != PROTOCOL_VERSION)
	{
		Netchan_OutOfBandPrint (NS_SERVER, net_from, "%c\nServer is version %4.2f.\n", A2C_PRINT, QW_VERSION);
		Com_Printf ("* rejected connect from version %i\n", version);
		return;
	}

	qport = atoi(Cmd_Argv(2));
	challenge = atoi(Cmd_Argv(3));

	// note an extra byte is needed to replace spectator key
	strlcpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-1);

	// see if the challenge is valid
	if (net_from.type != NA_LOOPBACK)
	{
		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, net_from, "%c\nBad challenge.\n", A2C_PRINT);
				return;
			}
		}
		if (i == MAX_CHALLENGES)
		{
			Netchan_OutOfBandPrint (NS_SERVER, net_from, "%c\nNo challenge for address.\n", A2C_PRINT);
			return;
		}
	}

	// check for password or spectator_password
	s = Info_ValueForKey (userinfo, "spectator");
	if (s[0] && strcmp(s, "0"))
	{
		if (sv_spectatorPassword.string[0] && 
			Q_stricmp(sv_spectatorPassword.string, "none") &&
			strcmp(sv_spectatorPassword.string, s) )
		{	// failed
			Com_Printf ("%s:spectator password failed\n", NET_AdrToString (net_from));
			Netchan_OutOfBandPrint (NS_SERVER, net_from, "%c\nrequires a spectator password\n\n", A2C_PRINT);
			return;
		}
		Info_RemoveKey (userinfo, "spectator");
		Info_SetValueForStarKey (userinfo, "*spectator", "1", MAX_INFO_STRING);
		spectator = true;
	}
	else
	{
		s = Info_ValueForKey (userinfo, "password");
		if (sv_password.string[0] && 
			Q_stricmp(sv_password.string, "none") &&
			strcmp(sv_password.string, s) )
		{
			Com_Printf ("%s:password failed\n", NET_AdrToString (net_from));
			Netchan_OutOfBandPrint (NS_SERVER, net_from, "%c\nserver requires a password\n\n", A2C_PRINT);
			return;
		}
		spectator = false;
		Info_RemoveKey (userinfo, "password");
	}

#ifdef MAUTH
    // Check that the client is allowed to connect...
	if( net_from.type != NA_LOOPBACK && !COM_CheckParm("-nomauth") )
    {
        authclient_t *authclient;
    
        // Try the auth token queue first...
        authclient = SV_AuthListFind(&authtokq, Info_ValueForKey(userinfo, "name"));
        if( authclient == NULL )
        {
            // Fall back to checking if they had already connected and are in the
            // client queue already (i.e. were on here before a map change)...
            authclient = SV_AuthListFind(&authclientq, Info_ValueForKey(userinfo, "name"));
            if( authclient == NULL )
            {
                // FIXME drop with reason
                Com_Printf ("MAUTH: Client %s not in a queue; connect refused.\n", Info_ValueForKey(userinfo, "name"));
                return;
            }
        }
        else
        {
            // They're valid, so move them to the main client queue from the
            // auth cache queue...
            SV_AuthListMove(&authtokq, &authclientq, authclient);
        }

        // Move to auth'd clients queue if they're valid...
        if( !authclient->valid )
        {
            // FIXME drop with reason
            Com_Printf ("MAUTH: Client %s not validated yet; connect refused.\n", Info_ValueForKey(userinfo, "name"));
            return;
        }
        
        //SV_AuthListPrint(&authtokq);
        //SV_AuthListPrint(&authclientq);
        
        Com_Printf ("MAUTH: Client %s connection allowed.\n", Info_ValueForKey(userinfo, "name"));
    }
    else
    {
        Com_Printf("MAUTH: loopback or disabled; allowing client connection.\n");
    }
#endif

	adr = net_from;

	// if there is already a slot for this ip, reuse it
	for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++)
	{
		if (cl->state == cs_free)
			continue;
		if (NET_CompareBaseAdr (adr, cl->netchan.remote_address)
			&& ( cl->netchan.qport == qport 
			|| adr.port == cl->netchan.remote_address.port ))
		{
			if (cl->state == cs_connected) {
				Com_Printf ("%s:dup connect\n", NET_AdrToString (adr));
				return;
			}

			Com_Printf ("%s:reconnect\n", NET_AdrToString (adr));
			if (cl->state == cs_spawned)
			{
				SV_DropClient (cl);
				SV_ClearReliable (cl);	// don't send the disconnect
			}
			cl->state = cs_free;
			break;
		}
	}

	// count up the clients and spectators and find an empty client slot
	clients = spectators = 0;
	newcl = NULL;
	for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++,cl++)
	{
		if (cl->state == cs_free) {
			if (!newcl)
				newcl = cl;		// grab first available slot
			continue;
		}
		if (cl->spectator)
			spectators++;
		else
			clients++;
	}

	// if at server limits, refuse connection
	if ( (spectator && spectators >= (int)maxspectators.value)
		|| (!spectator && clients >= (int)maxclients.value)
		|| !newcl)
	{
		Com_Printf ("%s:full connect\n", NET_AdrToString (adr));
		Netchan_OutOfBandPrint (NS_SERVER, adr, "%c\nserver is full\n\n", A2C_PRINT);
		return;
	}

	// build a new connection
	// accept the new client
	// this is the only place a client_t is ever initialized
	memset (newcl, 0, sizeof(*newcl));
	newcl->userid = SV_GenerateUserID();

	strlcpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo));

	Netchan_OutOfBandPrint (NS_SERVER, adr, "%c", S2C_CONNECTION );

	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;

	// spectator mode can ONLY be set at join time
	newcl->spectator = spectator;

	// extract extensions bits
	newcl->extensions = atoi(Info_ValueForKey(newcl->userinfo, "*z_ext"));
	Info_RemoveKey (newcl->userinfo, "*z_ext");

#ifdef VWEP_TEST
	newcl->extensions |= atoi(Info_ValueForKey(newcl->userinfo, "*vwtest")) ? Z_EXT_VWEP : 0;
	Info_RemoveKey (newcl->userinfo, "*vwtest");
#endif

	// See if the client is using a proxy. The best test I can come up with for now...
	newcl->uses_proxy = *Info_ValueForKey(newcl->userinfo, "Qizmo") ? true : false;

	edictnum = (newcl - svs.clients) + 1;
	ent = EDICT_NUM(edictnum);	
	ent->inuse = true;
	newcl->edict = ent;
	
	// parse some info from the info strings
	SV_ExtractFromUserinfo (newcl);

	// JACK: Init the floodprot stuff.
	for (i=0; i<10; i++)
		newcl->whensaid[i] = 0.0;
	newcl->whensaidhead = 0;
	newcl->lockedtill = 0;

	// call the progs to get default spawn parms for the new client
	PR_ExecuteProgram (pr_global_struct->SetNewParms);
	for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
		newcl->spawn_parms[i] = (&pr_global_struct->parm1)[i];

	if (newcl->spectator)
		Com_Printf ("Spectator %s connected\n", newcl->name);
	else
		Com_DPrintf ("Client %s connected\n", newcl->name);

	newcl->sendinfo = true;
}
Example #22
0
/*
=================
CL_ConnectionlessPacket

Responses to broadcasts, etc
=================
*/
void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
{
	char	*args;
	char	*c, buf[MAX_SYSPATH];
	int	len = sizeof( buf ), i = 0;
	netadr_t servadr;
	
	BF_Clear( msg );
	BF_ReadLong( msg ); // skip the -1

	args = BF_ReadStringLine( msg );

	Cmd_TokenizeString( args );
	c = Cmd_Argv( 0 );

	MsgDev( D_NOTE, "CL_ConnectionlessPacket: %s : %s\n", NET_AdrToString( from ), c );

	// server connection
	if( !Q_strcmp( c, "client_connect" ))
	{
		if( cls.state == ca_connected )
		{
			MsgDev( D_INFO, "Dup connect received. Ignored.\n");
			return;
		}

		Netchan_Setup( NS_CLIENT, &cls.netchan, from, net_qport->integer);
		BF_WriteByte( &cls.netchan.message, clc_stringcmd );
		BF_WriteString( &cls.netchan.message, "new" );
		cls.state = ca_connected;

		cl.validsequence = 0;		// haven't gotten a valid frame update yet
		cl.delta_sequence = -1;		// we'll request a full delta from the baseline
		cls.lastoutgoingcommand = -1;		// we don't have a backed up cmd history yet
		cls.nextcmdtime = host.realtime;	// we can send a cmd right away

		CL_StartupDemoHeader ();
	}
	else if( !Q_strcmp( c, "info" ))
	{
		// server responding to a status broadcast
		CL_ParseStatusMessage( from, msg );
	}
	else if( !Q_strcmp( c, "netinfo" ))
	{
		// server responding to a status broadcast
		CL_ParseNETInfoMessage( from, msg );
	}
	else if( !Q_strcmp( c, "cmd" ))
	{
		// remote command from gui front end
		if( !NET_IsLocalAddress( from ))
		{
			Msg( "Command packet from remote host. Ignored.\n" );
			return;
		}
#ifdef XASH_SDL
		SDL_RestoreWindow( host.hWnd );
#endif
		args = BF_ReadString( msg );
		Cbuf_AddText( args );
		Cbuf_AddText( "\n" );
	}
	else if( !Q_strcmp( c, "print" ))
	{
		// print command from somewhere
		Msg("remote: %s\n", BF_ReadString( msg ) );
	}
	else if( !Q_strcmp( c, "ping" ))
	{
		// ping from somewhere
		Netchan_OutOfBandPrint( NS_CLIENT, from, "ack" );
	}
	else if( !Q_strcmp( c, "challenge" ))
	{
		// challenge from the server we are connecting to
		cls.challenge = Q_atoi( Cmd_Argv( 1 ));
		CL_SendConnectPacket();
		return;
	}
	else if( !Q_strcmp( c, "echo" ))
	{
		// echo request from server
		Netchan_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv( 1 ));
	}
	else if( !Q_strcmp( c, "disconnect" ))
	{
		// a disconnect message from the server, which will happen if the server
		// dropped the connection but it is still getting packets from us
		CL_Disconnect();
		CL_ClearEdicts();
	}
	else if( !Q_strcmp( c, "f") )
	{
		// serverlist got from masterserver
		while( !msg->bOverflow )
		{
			servadr.type = NA_IP;
			// 4 bytes for IP
			BF_ReadBytes( msg, servadr.ip, sizeof( servadr.ip ));
			// 2 bytes for Port
			servadr.port = BF_ReadShort( msg );

			if( !servadr.port )
				break;

			MsgDev( D_INFO, "Found server: %s\n", NET_AdrToString( servadr ));

			NET_Config( true ); // allow remote

			Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION );
		}

		// execute at next frame preventing relation on fps
		Cbuf_AddText("menu_resetping\n");
	}
	else if( clgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len ))
	{
		// user out of band message (must be handled in CL_ConnectionlessPacket)
		if( len > 0 ) Netchan_OutOfBand( NS_SERVER, from, len, (byte *)buf );
	}
	else MsgDev( D_ERROR, "Bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args );
}
Example #23
0
/*
 * 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 = (int)strtol(Cmd_Argv(1), (char **)NULL, 10);

	if (version != PROTOCOL_VERSION)
	{
		Netchan_OutOfBandPrint(NS_SERVER, adr,
				"print\nServer is version %s.\n", YQ2VERSION);
		Com_DPrintf("    rejected connect from version %i\n", version);
		return;
	}

	qport = (int)strtol(Cmd_Argv(2), (char **)NULL, 10);

	challenge = (int)strtol(Cmd_Argv(3), (char **)NULL, 10);

	Q_strlcpy(userinfo, Cmd_Argv(4), sizeof(userinfo));

	/* 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))
			{
				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 */
	Q_strlcpy(newcl->userinfo, userinfo, sizeof(newcl->userinfo));
	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;
}
Example #24
0
/*
==================
SV_DirectConnect

A "connect" OOB command has been received
==================
*/
void SV_DirectConnect( netadr_t from, const Cmd::Args& args )
{
	char                userinfo[ MAX_INFO_STRING ];
	int                 i;
	client_t            *cl, *newcl;
	client_t            temp;
	sharedEntity_t      *ent;
	int                 clientNum;
	int                 version;
	int                 qport;
	int                 challenge;
	const char                *password;
	int                 startIndex;
	bool            denied;
	char                reason[ MAX_STRING_CHARS ];
	int                 count;
	const char          *ip;
#ifdef HAVE_GEOIP
	const char          *country = nullptr;
#endif

	if ( args.Argc() < 2 )
	{
		return;
	}

	Log::Debug( "SVC_DirectConnect ()" );

	Q_strncpyz( userinfo, args.Argv(1).c_str(), sizeof( userinfo ) );

	// DHM - Nerve :: Update Server allows any protocol to connect
	// NOTE TTimo: but we might need to store the protocol around for potential non http/ftp clients
	version = atoi( Info_ValueForKey( userinfo, "protocol" ) );

	if ( version != PROTOCOL_VERSION )
	{
		NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\nServer uses protocol version %i (yours is %i).", PROTOCOL_VERSION, version );
		Log::Debug( "    rejected connect from version %i", 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++ )
	{
		// DHM - Nerve :: This check was allowing clients to reconnect after zombietime(2 secs)
		//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 ) )
			{
				Log::Debug( "%s: reconnect rejected: too soon", NET_AdrToString( from ) );
				return;
			}

			break;
		}
	}

	if ( NET_IsLocalAddress( from ) )
	{
		ip = "localhost";
	}
	else
	{
		ip = NET_AdrToString( from );
	}

	if ( ( strlen( ip ) + strlen( userinfo ) + 4 ) >= MAX_INFO_STRING )
	{
		NET_OutOfBandPrint( netsrc_t::NS_SERVER, from,
		                    "print\nUserinfo string length exceeded.  "
		                    "Try removing setu cvars from your config." );
		return;
	}

	Info_SetValueForKey( userinfo, "ip", ip, false );

	// see if the challenge is valid (local 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( netsrc_t::NS_SERVER, from, "print\n[err_dialog]No or bad challenge for address." );
			return;
		}

		// force the IP address key/value pair, so the game can filter based on it
		Info_SetValueForKey( userinfo, "ip", NET_AdrToString( from ), false );

		if ( svs.challenges[ i ].firstPing == 0 )
		{
			ping = svs.time - svs.challenges[ i ].pingTime;
			svs.challenges[ i ].firstPing = ping;
		}
		else
		{
			ping = svs.challenges[ i ].firstPing;
		}

#ifdef HAVE_GEOIP
		country = NET_GeoIP_Country( &from );

		if ( country )
		{
			Log::Notice( "Client %i connecting from %s with %i challenge ping\n", i, country, ping );
		}
		else
		{
			Log::Notice( "Client %i connecting from somewhere unknown with %i challenge ping\n", i, ping );
		}
#else
		Log::Notice( "Client %i connecting with %i challenge ping\n", i, ping );
#endif

		svs.challenges[ i ].connected = true;

		// never reject a LAN client based on ping
		if ( !Sys_IsLANAddress( from ) )
		{
			if ( sv_minPing->value && ping < sv_minPing->value )
			{
				NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n[err_dialog]Server is for high pings only" );
				Log::Debug( "Client %i rejected on a too low ping", i );
				return;
			}

			if ( sv_maxPing->value && ping > sv_maxPing->value )
			{
				NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n[err_dialog]Server is for low pings only" );
				Log::Debug( "Client %i rejected on a too high ping: %i", i, ping );
				return;
			}
		}
	}
	else
	{
		// force the "ip" info key to "localhost"
		Info_SetValueForKey( userinfo, "ip", "localhost", false );
	}

	newcl = &temp;
	memset( newcl, 0, sizeof( client_t ) );

	// if there is already a slot for this IP address, reuse it
	for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
	{
		if ( cl->state == clientState_t::CS_FREE )
		{
			continue;
		}

		if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress )
		     && ( cl->netchan.qport == qport
		          || from.port == cl->netchan.remoteAddress.port ) )
		{
			Log::Notice( "%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 = nullptr;

	for ( i = startIndex; i < sv_maxclients->integer; i++ )
	{
		cl = &svs.clients[ i ];

		if ( cl->state == clientState_t::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 ( SV_IsBot(cl) )
				{
					count++;
				}
			}

			// if they're all bots
			if ( count >= sv_maxclients->integer - startIndex )
			{
				SV_DropClient( &svs.clients[ sv_maxclients->integer - 1 ], "only bots on server" );
				newcl = &svs.clients[ sv_maxclients->integer - 1 ];
			}
			else
			{
				Com_Error( errorParm_t::ERR_FATAL, "server is full on local connect" );
			}
		}
		else
		{
			NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n%s", sv_fullmsg->string );
			Log::Debug( "Rejected a connection." );
			return;
		}
	}

	// we got a newcl, so reset the reliableSequence and reliableAcknowledge
	cl->reliableAcknowledge = 0;
	cl->reliableSequence = 0;

gotnewcl:
	// build a new connection
	// accept the new client
	// this is the only place a client_t is ever initialized
	*newcl = std::move(temp);
	clientNum = newcl - svs.clients;
	ent = SV_GentityNum( clientNum );
	newcl->gentity = ent;
	ent->r.svFlags = 0;

#ifdef HAVE_GEOIP

	if ( country )
	{
		Info_SetValueForKey( userinfo, "geoip", country, false );
	}
#endif

	// save the challenge
	newcl->challenge = challenge;

	// save the address
	Netchan_Setup( netsrc_t::NS_SERVER, &newcl->netchan, from, qport );
	// init the netchan queue

	// Save the pubkey
	Q_strncpyz( newcl->pubkey, Info_ValueForKey( userinfo, "pubkey" ), sizeof( newcl->pubkey ) );
	Info_RemoveKey( userinfo, "pubkey", false );
	// save the userinfo
	Q_strncpyz( newcl->userinfo, userinfo, sizeof( newcl->userinfo ) );

	// get the game a chance to reject this connection or modify the userinfo
	denied = gvm.GameClientConnect( reason, sizeof( reason ), clientNum, true, false );  // firstTime = true

	if ( denied )
	{
		NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n[err_dialog]%s", reason );
		Log::Debug( "Game rejected a connection: %s.", reason );
		return;
	}

	SV_UserinfoChanged( newcl );

	// DHM - Nerve :: Clear out firstPing now that client is connected
	svs.challenges[ i ].firstPing = 0;

	// send the connect packet to the client
	NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "connectResponse" );

	Log::Debug( "Going from CS_FREE to CS_CONNECTED for %s", newcl->name );

	newcl->state = clientState_t::CS_CONNECTED;
	newcl->nextSnapshotTime = svs.time;
	newcl->lastPacketTime = svs.time;
	newcl->lastConnectTime = svs.time;

	// when we receive the first packet from the client, we will
	// notice that it is from a different serverid and that the
	// gamestate message was not just sent, forcing a retransmit
	newcl->gamestateMessageNum = -1;

	// if this was the first client on the server, or the last client
	// the server can hold, send a heartbeat to the master.
	count = 0;

	for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
	{
		if ( svs.clients[ i ].state >= clientState_t::CS_CONNECTED )
		{
			count++;
		}
	}

	if ( count == 1 || count == sv_maxclients->integer )
	{
		SV_Heartbeat_f();
	}
}