示例#1
0
文件: query.c 项目: deurk/qwfwd
void SVC_QRY_ParseMasterReply(void)
{
    int				i, c;
	master_t		*m;
	int				ret = net_message.cursize;
	unsigned char	*answer = net_message.data; // not the smartest way, but why copy from one place to another...

	// no point to parse it, we do not query masters
	if (!masters_query->integer)
	{
		Sys_DPrintf("master server reply ignored\n");
		return;
	}

	Sys_DPrintf ("master server reply from %s:%d\n", inet_ntoa(net_from.sin_addr), (int)ntohs(net_from.sin_port));

	// is it reply from registered master server or someone trying to do some evil things?
	for (i = 0, m = masters.master; i < MAX_MASTERS; i++, m++)
	{
		if (m->state != ms_used)
			continue; // master slot not used

		if (NET_CompareAddress(&net_from, &m->addr))
		{
			// OK - it is reply from registered master server
			m->next_query = time(NULL) + QW_MASTER_QUERY_TIME; // delay next query for some time
			break;
		}
	}

	if (i >= MAX_MASTERS)
	{
		Sys_Printf("Reply from not registered master server\n");
		return;
	}

	Sys_DPrintf("master server returned %d bytes\n", ret);

	for (c = 0, i = 6; i + 5 < ret; i += 6, c++)
	{
		char ip[64];
		int port = 256 * (int)answer[i+4] + (int)answer[i+5];

		snprintf(ip, sizeof(ip), "%u.%u.%u.%u",
			(int)answer[i+0], (int)answer[i+1],
			(int)answer[i+2], (int)answer[i+3]);

		if (developer->integer > 1)
			Sys_DPrintf("SERVER: %4d %s:%d\n", c, ip, port);

		QRY_SV_new(ip, port, true);
	}
}
示例#2
0
文件: peer.c 项目: deurk/qwfwd
peer_t	*FWD_peer_by_addr(struct sockaddr_in *from)
{
	peer_t *p;

	for (p = peers; p; p = p->next)
	{
		if (NET_CompareAddress(&p->from, from))
			return p;
	}

	return NULL;
}
示例#3
0
文件: query.c 项目: deurk/qwfwd
static master_t	*QRY_Master_ByAddr(struct sockaddr_in *addr)
{
	int						i;
	master_t				*m;

	for (i = 0, m = masters.master; i < MAX_MASTERS; i++, m++)
	{
		if (m->state != ms_used)
			continue; // master slot unused

		if (NET_CompareAddress(addr, &m->addr))
			return m;
	}

	return NULL;
}
示例#4
0
/*
* SVC_DirectConnect
* A connection request that did not come from the master
*/
static void SVC_DirectConnect( const socket_t *socket, const netadr_t *address )
{
#ifdef TCP_ALLOW_CONNECT
	int incoming = 0;
#endif
	char userinfo[MAX_INFO_STRING];
	client_t *cl, *newcl;
	int i, version, game_port, challenge;
	int previousclients;
	int session_id;
	char *session_id_str;
	unsigned int ticket_id;
	qboolean tv_client;

	Com_DPrintf( "SVC_DirectConnect (%s)\n", Cmd_Args() );

	version = atoi( Cmd_Argv( 1 ) );
	if( version != APP_PROTOCOL_VERSION )
	{
		if( version <= 6 )
		{            // before reject packet was added
			Netchan_OutOfBandPrint( socket, address, "print\nServer is version %4.2f. Protocol %3i\n",
				APP_VERSION, APP_PROTOCOL_VERSION );
		}
		else
		{
			Netchan_OutOfBandPrint( socket, address,
				"reject\n%i\n%i\nServer and client don't have the same version\n", DROP_TYPE_GENERAL, 0 );
		}
		Com_DPrintf( "    rejected connect from protocol %i\n", version );
		return;
	}

	game_port = atoi( Cmd_Argv( 2 ) );
	challenge = atoi( Cmd_Argv( 3 ) );
	tv_client = ( atoi( Cmd_Argv( 5 ) ) & 1 ? qtrue : qfalse );

	if( !Info_Validate( Cmd_Argv( 4 ) ) )
	{
		Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nInvalid userinfo string\n", DROP_TYPE_GENERAL, 0 );
		Com_DPrintf( "Connection from %s refused: invalid userinfo string\n", NET_AddressToString( address ) );
		return;
	}

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

	// force the IP key/value pair so the game can filter based on ip
	if( !Info_SetValueForKey( userinfo, "socket", NET_SocketTypeToString( socket->type ) ) )
	{
		Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (socket)\n",
			DROP_TYPE_GENERAL, 0 );
		Com_DPrintf( "Connection from %s refused: couldn't set userinfo (socket)\n", NET_AddressToString( address ) );
		return;
	}
	if( !Info_SetValueForKey( userinfo, "ip", NET_AddressToString( address ) ) )
	{
		Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (ip)\n",
			DROP_TYPE_GENERAL, 0 );
		Com_DPrintf( "Connection from %s refused: couldn't set userinfo (ip)\n", NET_AddressToString( address ) );
		return;
	}

	if( Cmd_Argc() >= 7 )
	{
		// we have extended information, ticket-id and session-id
		Com_Printf("Extended information %s\n", Cmd_Argv(6) );
		ticket_id = (unsigned int)atoi( Cmd_Argv(6) );
		session_id_str = Info_ValueForKey( userinfo, "cl_mm_session" );
		if( session_id_str != NULL )
			session_id = atoi( session_id_str );
		else
			session_id = 0;
	}
	else
	{
		ticket_id = 0;
		session_id = 0;
	}

#ifdef TCP_ALLOW_CONNECT
	if( socket->type == SOCKET_TCP )
	{
		// find the connection
		for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ )
		{
			if( !svs.incoming[i].active )
				continue;

			if( NET_CompareAddress( &svs.incoming[i].address, address ) && socket == &svs.incoming[i].socket )
				break;
		}
		if( i == MAX_INCOMING_CONNECTIONS )
		{
			Com_Error( ERR_FATAL, "Incoming connection not found.\n" );
			return;
		}
		incoming = i;
	}
#endif

	// see if the challenge is valid
	for( i = 0; i < MAX_CHALLENGES; i++ )
	{
		if( NET_CompareBaseAddress( address, &svs.challenges[i].adr ) )
		{
			if( challenge == svs.challenges[i].challenge )
			{
				svs.challenges[i].challenge = 0; // wsw : r1q2 : reset challenge
				svs.challenges[i].time = 0;
				NET_InitAddress( &svs.challenges[i].adr, NA_NOTRANSMIT );
				break; // good
			}
			Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nBad challenge\n",
				DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT );
			return;
		}
	}
	if( i == MAX_CHALLENGES )
	{
		Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nNo challenge for address\n",
			DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT );
		return;
	}

	//r1: limit connections from a single IP
	if( sv_iplimit->integer )
	{
		previousclients = 0;
		for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
		{
			if( cl->state == CS_FREE )
				continue;
			if( NET_CompareBaseAddress( address, &cl->netchan.remoteAddress ) )
			{
				//r1: zombies are less dangerous
				if( cl->state == CS_ZOMBIE )
					previousclients++;
				else
					previousclients += 2;
			}
		}

		if( previousclients >= sv_iplimit->integer * 2 )
		{
			Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nToo many connections from your host\n", DROP_TYPE_GENERAL,
				DROP_FLAG_AUTORECONNECT );
			Com_DPrintf( "%s:connect rejected : too many connections\n", NET_AddressToString( address ) );
			return;
		}
	}

	newcl = NULL;

	// 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_CompareAddress( address, &cl->netchan.remoteAddress ) ||
			( NET_CompareBaseAddress( address, &cl->netchan.remoteAddress ) && cl->netchan.game_port == game_port ) )
		{
			if( !NET_IsLocalAddress( address ) &&
				( svs.realtime - cl->lastconnect ) < (unsigned)( sv_reconnectlimit->integer * 1000 ) )
			{
				Com_DPrintf( "%s:reconnect rejected : too soon\n", NET_AddressToString( address ) );
				return;
			}
			Com_Printf( "%s:reconnect\n", NET_AddressToString( address ) );
			newcl = cl;
			break;
		}
	}

	// find a client slot
	if( !newcl )
	{
		for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
		{
			if( cl->state == CS_FREE )
			{
				newcl = cl;
				break;
			}
			// overwrite fakeclient if no free spots found
			if( cl->state && cl->edict && ( cl->edict->r.svflags & SVF_FAKECLIENT ) )
				newcl = cl;
		}
		if( !newcl )
		{
			Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nServer is full\n", DROP_TYPE_GENERAL,
				DROP_FLAG_AUTORECONNECT );
			Com_DPrintf( "Server is full. Rejected a connection.\n" );
			return;
		}
		if( newcl->state && newcl->edict && ( newcl->edict->r.svflags & SVF_FAKECLIENT ) )
			SV_DropClient( newcl, DROP_TYPE_GENERAL, "Need room for a real player" );
	}

	// get the game a chance to reject this connection or modify the userinfo
	if( !SV_ClientConnect( socket, address, newcl, userinfo, game_port, challenge, qfalse, 
		tv_client, ticket_id, session_id ) )
	{
		char *rejtype, *rejflag, *rejtypeflag, *rejmsg;

		rejtype = Info_ValueForKey( userinfo, "rejtype" );
		if( !rejtype )
			rejtype = "0";
		rejflag = Info_ValueForKey( userinfo, "rejflag" );
		if( !rejflag )
			rejflag = "0";
		// hax because Info_ValueForKey can only be called twice in a row
		rejtypeflag = va( "%s\n%s", rejtype, rejflag );

		rejmsg = Info_ValueForKey( userinfo, "rejmsg" );
		if( !rejmsg )
			rejmsg = "Game module rejected connection";

		Netchan_OutOfBandPrint( socket, address, "reject\n%s\n%s\n", rejtypeflag, rejmsg );

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

	// send the connect packet to the client
	Netchan_OutOfBandPrint( socket, address, "client_connect\n%s", newcl->session );

	// free the incoming entry
#ifdef TCP_ALLOW_CONNECT
	if( socket->type == SOCKET_TCP )
	{
		svs.incoming[incoming].active = qfalse;
		svs.incoming[incoming].socket.open = qfalse;
	}
#endif
}
示例#5
0
/**
 * Responds to a Steam server query.
 *
 * @param s       query string
 * @param socket  response socket
 * @param address response address
 * @param inmsg   message for arguments
 * @return whether the request was handled as a Steam query
 */
bool SV_SteamServerQuery( const char *s, const socket_t *socket, const netadr_t *address, msg_t *inmsg )
{
#if APP_STEAMID
	if( sv.state < ss_loading || sv.state > ss_game )
		return false; // server not running

	if( ( !sv_public->integer && !NET_IsLANAddress( address ) ) || ( sv_maxclients->integer == 1 ) )
		return false;

	if( !strcmp( s, "i" ) )
	{
		// ping
		const char pingResponse[] = "j00000000000000";
		Netchan_OutOfBand( socket, address, sizeof( pingResponse ), ( const uint8_t * )pingResponse );
		return true;
	}

	if( !strcmp( s, "W" ) || !strcmp( s, "U\xFF\xFF\xFF\xFF" ) )
	{
		// challenge - security feature, but since we don't send multiple packets always return 0
		const uint8_t challengeResponse[] = { 'A', 0, 0, 0, 0 };
		Netchan_OutOfBand( socket, address, sizeof( challengeResponse ), ( const uint8_t * )challengeResponse );
		return true;
	}

	if( !strcmp( s, "TSource Engine Query" ) )
	{
		// server info
		char hostname[MAX_INFO_VALUE];
		char gamedir[MAX_QPATH];
		char gamename[128];
		char version[32];
		char tags[MAX_STEAMQUERY_TAG_STRING];
		int i, players = 0, bots = 0, maxclients = 0;
		int flags = 0x80 | 0x01; // game port | game ID containing app ID
		client_t *cl;
		msg_t msg;
		uint8_t msgbuf[MAX_STEAMQUERY_PACKETLEN - sizeof( int32_t )];

		if( sv_showInfoQueries->integer )
			Com_Printf( "Steam Info Packet %s\n", NET_AddressToString( address ) );

		Q_strncpyz( hostname, COM_RemoveColorTokens( sv_hostname->string ), sizeof( hostname ) );
		if( !hostname[0] )
			Q_strncpyz( hostname, sv_hostname->dvalue, sizeof( hostname ) );
		Q_strncpyz( gamedir, FS_GameDirectory(), sizeof( gamedir ) );

		Q_strncpyz( gamename, APPLICATION, sizeof( gamename ) );
		if( Cvar_Value( "g_instagib" ) )
			Q_strncatz( gamename, " IG", sizeof( gamename ) );
		if( sv.configstrings[CS_GAMETYPETITLE][0] || sv.configstrings[CS_GAMETYPENAME][0] )
		{
			Q_strncatz( gamename, " ", sizeof( gamename ) );
			Q_strncatz( gamename,
				sv.configstrings[sv.configstrings[CS_GAMETYPETITLE][0] ? CS_GAMETYPETITLE : CS_GAMETYPENAME],
				sizeof( gamename ) );
		}

		for( i = 0; i < sv_maxclients->integer; i++ )
		{
			cl = &svs.clients[i];
			if( cl->state >= CS_CONNECTED )
			{
				if( cl->tvclient ) // exclude TV from the max players count
					continue;
				if( cl->edict->r.svflags & SVF_FAKECLIENT )
					bots++;
				players++;
			}
			maxclients++;
		}

		Q_snprintfz( version, sizeof( version ), "%i.%i.0.0", APP_VERSION_MAJOR, APP_VERSION_MINOR );

		SV_GetSteamTags( tags );
		if( tags[0] )
			flags |= 0x20;

		MSG_Init( &msg, msgbuf, sizeof( msgbuf ) );
		MSG_WriteByte( &msg, 'I' );
		MSG_WriteByte( &msg, APP_PROTOCOL_VERSION );
		MSG_WriteString( &msg, hostname );
		MSG_WriteString( &msg, sv.mapname );
		MSG_WriteString( &msg, gamedir );
		MSG_WriteString( &msg, gamename );
		MSG_WriteShort( &msg, 0 ); // app ID specified later
		MSG_WriteByte( &msg, min( players, 99 ) );
		MSG_WriteByte( &msg, min( maxclients, 99 ) );
		MSG_WriteByte( &msg, min( bots, 99 ) );
		MSG_WriteByte( &msg, ( dedicated && dedicated->integer ) ? 'd' : 'l' );
		MSG_WriteByte( &msg, STEAMQUERY_OS );
		MSG_WriteByte( &msg, Cvar_String( "password" )[0] ? 1 : 0 );
		MSG_WriteByte( &msg, 0 ); // VAC insecure
		MSG_WriteString( &msg, version );
		MSG_WriteByte( &msg, flags );
		// port
		MSG_WriteShort( &msg, sv_port->integer );
		// tags
		if( flags & 0x20 )
			MSG_WriteString( &msg, tags );
		// 64-bit game ID - needed to specify app ID
		MSG_WriteLong( &msg, APP_STEAMID & 0xffffff );
		MSG_WriteLong( &msg, 0 );
		Netchan_OutOfBand( socket, address, msg.cursize, msg.data );
		return true;
	}

	if( s[0] == 'U' )
	{
		// players
		msg_t msg;
		uint8_t msgbuf[MAX_STEAMQUERY_PACKETLEN - sizeof( int32_t )];
		int i, players = 0;
		client_t *cl;
		char name[MAX_NAME_BYTES];
		unsigned int time = Sys_Milliseconds();

		if( sv_showInfoQueries->integer )
			Com_Printf( "Steam Players Packet %s\n", NET_AddressToString( address ) );

		MSG_Init( &msg, msgbuf, sizeof( msgbuf ) );
		MSG_WriteByte( &msg, 'D' );
		MSG_WriteByte( &msg, 0 );

		for( i = 0; i < sv_maxclients->integer; i++ )
		{
			cl = &svs.clients[i];
			if( ( cl->state < CS_CONNECTED ) || cl->tvclient )
				continue;

			Q_strncpyz( name, COM_RemoveColorTokens( cl->name ), sizeof( name ) );
			if( ( msg.cursize + 10 + strlen( name ) ) > sizeof( msgbuf ) )
				break;

			MSG_WriteByte( &msg, i );
			MSG_WriteString( &msg, name );
			MSG_WriteLong( &msg, cl->edict->r.client->r.frags );
			MSG_WriteFloat( &msg, ( float )( time - cl->lastconnect ) * 0.001f );

			players++;
			if( players == 99 )
				break;
		}

		msgbuf[1] = players;
		Netchan_OutOfBand( socket, address, msg.cursize, msg.data );
		return true;
	}

	if( !strcmp( s, "s" ) )
	{
		// master server query, terminated by \n, followed by the challenge
		int i;
		bool fromMaster = false;
		int challenge;
		char gamedir[MAX_QPATH], basedir[MAX_QPATH], tags[MAX_STEAMQUERY_TAG_STRING];
		int players = 0, bots = 0, maxclients = 0;
		client_t *cl;
		char msg[MAX_STEAMQUERY_PACKETLEN];

		for( i = 0; i < MAX_MASTERS; i++ )
		{
			if( sv_masters[i].steam && NET_CompareAddress( address, &sv_masters[i].address ) )
			{
				fromMaster = true;
				break;
			}
		}
		if( !fromMaster )
			return true;

		if( sv_showInfoQueries->integer )
			Com_Printf( "Steam Master Server Info Packet %s\n", NET_AddressToString( address ) );

		challenge = MSG_ReadLong( inmsg );

		Q_strncpyz( gamedir, FS_GameDirectory(), sizeof( gamedir ) );
		Q_strncpyz( basedir, FS_BaseGameDirectory(), sizeof( basedir ) );
		SV_GetSteamTags( tags );

		for( i = 0; i < sv_maxclients->integer; i++ )
		{
			cl = &svs.clients[i];
			if( cl->state >= CS_CONNECTED )
			{
				if( cl->tvclient ) // exclude TV from the max players count
					continue;
				if( cl->edict->r.svflags & SVF_FAKECLIENT )
					bots++;
				players++;
			}
			maxclients++;
		}

		Q_snprintfz( msg, sizeof( msg ),
			"0\n\\protocol\\7\\challenge\\%i" // protocol must be 7 to match Source
			"\\players\\%i\\max\\%i\\bots\\%i"
			"\\gamedir\\%s\\map\\%s"
			"\\password\\%i\\os\\%c"
			"\\lan\\%i\\region\\255"
			"%s%s"
			"\\type\\%c\\secure\\0"
			"\\version\\%i.%i.0.0"
			"\\product\\%s\n",
			challenge,
			min( players, 99 ), min( maxclients, 99 ), min( bots, 99 ),
			gamedir, sv.mapname,
			Cvar_String( "password" )[0] ? 1 : 0, STEAMQUERY_OS,
			sv_public->integer ? 0 : 1,
			tags[0] ? "\\gametype\\" /* legacy - "gametype", not "tags" */ : "", tags,
			( dedicated && dedicated->integer ) ? 'd' : 'l',
			APP_VERSION_MAJOR, APP_VERSION_MINOR,
			basedir );
		NET_SendPacket( socket, ( const uint8_t * )msg, strlen( msg ), address );

		return true;
	}

	if( s[0] == 'O' )
	{
		// out of date message
		static bool printed = false;
		if( !printed )
		{
			int i;
			for( i = 0; i < MAX_MASTERS; i++ )
			{
				if( sv_masters[i].steam && NET_CompareAddress( address, &sv_masters[i].address ) )
				{
					Com_Printf( "Server is out of date and cannot be added to the Steam master servers.\n" );
					printed = true;
					return true;
				}
			}
		}
		return true;
	}
#endif

	return false;
}
示例#6
0
/*
* TV_Downstream_DirectConnect
* A upstream request that did not come from the master
*/
static void TV_Downstream_DirectConnect( const socket_t *socket, const netadr_t *address )
{
#ifdef TCP_ALLOW_TVCONNECT
	int incoming = 0;
#endif
	char userinfo[MAX_INFO_STRING], *name;
	client_t *cl, *newcl;
	int i, version, game_port, challenge;
	bool tv_client;

	version = atoi( Cmd_Argv( 1 ) );
	if( version != APP_PROTOCOL_VERSION )
	{
		if( version <= 6 )
		{            // before reject packet was added
			Netchan_OutOfBandPrint( socket, address, "print\nServer is version %4.2f. Protocol %3i\n",
				APP_VERSION, APP_PROTOCOL_VERSION );
		}
		else
		{
			Netchan_OutOfBandPrint( socket, address,
				"reject\n%i\n%i\nServer and client don't have the same version\n", DROP_TYPE_GENERAL, 0 );
		}
		return;
	}

	game_port = atoi( Cmd_Argv( 2 ) );
	challenge = atoi( Cmd_Argv( 3 ) );
	tv_client = ( atoi( Cmd_Argv( 5 ) ) & 1 ? true : false );

	if( !Info_Validate( Cmd_Argv( 4 ) ) )
	{
		Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nInvalid userinfo string\n", DROP_TYPE_GENERAL, 0 );
		Com_DPrintf( "Upstream from %s refused: invalid userinfo string\n", NET_AddressToString( address ) );
		return;
	}

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

	// force the IP key/value pair so the game can filter based on ip
	if( !Info_SetValueForKey( userinfo, "socket", NET_SocketTypeToString( socket->type ) ) )
	{
		Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (socket)\n",
			DROP_TYPE_GENERAL, 0 );
		Com_DPrintf( "Upstream from %s refused: couldn't set userinfo (socket)\n", NET_AddressToString( address ) );
		return;
	}
	if( !Info_SetValueForKey( userinfo, "ip", NET_AddressToString( address ) ) )
	{
		Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (ip)\n",
			DROP_TYPE_GENERAL, 0 );
		Com_DPrintf( "Upstream from %s refused: couldn't set userinfo (ip)\n", NET_AddressToString( address ) );
		return;
	}

	// we handle name ourselves here, since tv module doesn't know about all the players
	name = TV_Downstream_FixName( Info_ValueForKey( userinfo, "name" ), NULL );
	if( !Info_SetValueForKey( userinfo, "name", name ) )
	{
		Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (name)\n",
			DROP_TYPE_GENERAL, 0 );
		Com_DPrintf( "Upstream from %s refused: couldn't set userinfo (name)\n", NET_AddressToString( address ) );
		return;
	}

#ifdef TCP_ALLOW_TVCONNECT
	if( socket->type == SOCKET_TCP )
	{
		// find the upstream
		for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ )
		{
			if( !tvs.incoming[i].active )
				continue;

			if( NET_CompareAddress( &tvs.incoming[i].address, address ) && socket == &tvs.incoming[i].socket )
				break;
		}
		if( i == MAX_INCOMING_CONNECTIONS )
		{
			Com_Error( ERR_FATAL, "Incoming upstream not found.\n" );
			return;
		}
		incoming = i;
	}
#endif

	// see if the challenge is valid
	for( i = 0; i < MAX_CHALLENGES; i++ )
	{
		if( NET_CompareBaseAddress( address, &tvs.challenges[i].adr ) )
		{
			if( challenge == tvs.challenges[i].challenge )
			{
				tvs.challenges[i].challenge = 0; // wsw : r1q2 : reset challenge
				tvs.challenges[i].time = 0;
				NET_InitAddress( &tvs.challenges[i].adr, NA_NOTRANSMIT );
				break; // good
			}
			Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nBad challenge\n",
				DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT );
			return;
		}
	}
	if( i == MAX_CHALLENGES )
	{
		Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nNo challenge for address\n",
			DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT );
		return;
	}

	newcl = NULL;

	// if there is already a slot for this ip, reuse it
	for( i = 0, cl = tvs.clients; i < tv_maxclients->integer; i++, cl++ )
	{
		if( cl->state == CS_FREE )
			continue;

		if( NET_CompareAddress( address, &cl->netchan.remoteAddress ) ||
			( NET_CompareBaseAddress( address, &cl->netchan.remoteAddress ) && cl->netchan.game_port == game_port ) )
		{
			if( !NET_IsLocalAddress( address ) &&
				( tvs.realtime - cl->lastconnect ) < (unsigned)( tv_reconnectlimit->integer * 1000 ) )
			{
				return;
			}
			newcl = cl;
			break;
		}
	}

	// find a client slot
	if( !newcl )
	{
		for( i = 0, cl = tvs.clients; i < tv_maxclients->integer; i++, cl++ )
		{
			if( cl->state == CS_FREE )
			{
				newcl = cl;
				break;
			}
		}
		if( !newcl )
		{
			Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nServer is full\n", DROP_TYPE_GENERAL,
				DROP_FLAG_AUTORECONNECT );
			return;
		}
	}

	// get the game a chance to reject this upstream or modify the userinfo
	if( !TV_Downstream_ClientConnect( socket, address, newcl, userinfo, game_port, challenge, tv_client ) )
	{
		char *rejtypeflag, *rejmsg;

		// hax because Info_ValueForKey can only be called twice in a row
		rejtypeflag = va( "%s\n%s", Info_ValueForKey( userinfo, "rejtype" ), Info_ValueForKey( userinfo, "rejflag" ) );
		rejmsg = Info_ValueForKey( userinfo, "rejmsg" );

		Netchan_OutOfBandPrint( socket, address, "reject\n%s\n%s\n", rejtypeflag, rejmsg );
		return;
	}

	// send the connect packet to the client
	Netchan_OutOfBandPrint( socket, address, "client_connect" );

	// free the incoming entry
#ifdef TCP_ALLOW_TVCONNECT
	if( socket->type == SOCKET_TCP )
	{
		tvs.incoming[incoming].active = false;
		tvs.incoming[incoming].socket.open = false;
	}
#endif
}
示例#7
0
文件: peer.c 项目: deurk/qwfwd
static void FWD_network_update(void)
{
	fd_set rfds;
	struct timeval tv;
	int retval;
	int i1;
	peer_t *p;

	FD_ZERO(&rfds);

	// select on main server socket
	FD_SET(net_socket, &rfds);
	i1 = net_socket + 1;

	for (p = peers; p; p = p->next)
	{
		// select on peers sockets
		FD_SET(p->s, &rfds);
		if (p->s >= i1)
			i1 = p->s + 1;
	}

// if not DLL - read stdin
#ifndef APP_DLL
	#ifndef _WIN32
	// try read stdin only if connected to a terminal.
	if (isatty(STDIN) && isatty(STDOUT))
	{
		FD_SET(STDIN, &rfds);
		if (STDIN >= i1)
			i1 = STDIN + 1;
	}
	#endif // _WIN32
#endif

	/* Sleep for some time, wake up immidiately if there input packet. */
	tv.tv_sec = 0;
	tv.tv_usec = 100000; // 100 ms
	retval = select(i1, &rfds, (fd_set *)0, (fd_set *)0, &tv);

	// read console input.
	// NOTE: we do not do that if we are in DLL mode...
	Sys_ReadSTDIN(&ps, rfds);

	if (retval <= 0)
		return;

	// if we have input packet on main server/proxy socket, then read it
	if(FD_ISSET(net_socket, &rfds))
	{
		qbool connectionless;
		int cnt;

		// read it
		for(;;)
		{
			if (!NET_GetPacket(net_socket, &net_message))
				break;

			// check for bans.
			if (SV_IsBanned(&net_from))
				continue;

			if (net_message.cursize == 1 && net_message.data[0] == A2A_ACK)
			{
				QRY_SV_PingReply();

				continue;
			}

			MSG_BeginReading();
			connectionless = (MSG_ReadLong() == -1);

			if (connectionless)
			{
				if (MSG_BadRead())
					continue;

				if (!SV_ConnectionlessPacket())
					continue; // seems we do not need forward it
			}

			// search in peers
			for (p = peers; p; p = p->next)
			{
				// we have this peer already, so forward/send packet to remote server
				if (NET_CompareAddress(&p->from, &net_from))
					break;
			}

			// peer was not found
			if (!p)
				continue;

			// forward data to the server/proxy
			if (p->ps >= ps_connected)
			{
				cnt = 1; // one packet by default

				// check for "drop" aka client disconnect,
				// first 10 bytes for NON connectionless packet is netchan related shit in QW
				if (p->proto == pr_qw && !connectionless && net_message.cursize > 10 && net_message.data[10] == clc_stringcmd)
				{
					if (!strcmp((char*)net_message.data + 10 + 1, "drop"))
					{
//						Sys_Printf("peer drop detected\n");
						p->ps = ps_drop; // drop peer ASAP
						cnt = 3; // send few packets due to possibile packet lost
					}
				}

				for ( ; cnt > 0; cnt--)
					NET_SendPacket(p->s, net_message.cursize, net_message.data, &p->to);
			}

			time(&p->last);
		}
	}

	// now lets check peers sockets, perhaps we have input packets too
	for (p = peers; p; p = p->next)
	{
		if(FD_ISSET(p->s, &rfds))
		{
			// yeah, we have packet, read it then
			for (;;)
			{
				if (!NET_GetPacket(p->s, &net_message))
					break;

				// check for bans.
				if (SV_IsBanned(&net_from))
					continue;

				// we should check is this packet from remote server, this may be some evil packet from haxors...
				if (!NET_CompareAddress(&p->to, &net_from))
					continue;

				MSG_BeginReading();
				if (MSG_ReadLong() == -1)
				{
					if (MSG_BadRead())
						continue;

					if (!CL_ConnectionlessPacket(p))
						continue; // seems we do not need forward it

					NET_SendPacket(net_socket, net_message.cursize, net_message.data, &p->from);
					continue;
				}

				if (p->ps >= ps_connected)
					NET_SendPacket(net_socket, net_message.cursize, net_message.data, &p->from);

// qqshka: commented out
//				time(&p->last);

			} // for (;;)
		} // if(FD_ISSET(p->s, &rfds))

		if (p->ps == ps_challenge)
		{
			// send challenge time to time
			if (time(NULL) - p->connect > 2)
			{
				p->connect = time(NULL);
				Netchan_OutOfBandPrint(p->s, &p->to, "getchallenge%s", p->proto == pr_qw ? "\n" : "");
			}
		}
	} // for (p = peers; p; p = p->next)
}