Пример #1
0
/*
* SVC_GetChallenge
* 
* Returns a challenge number that can be used
* in a subsequent client_connect command.
* We do this to prevent denial of service attacks that
* flood the server with invalid connection IPs.  With a
* challenge, they must give a valid IP address.
*/
static void SVC_GetChallenge( const socket_t *socket, const netadr_t *address )
{
	int i;
	int oldest;
	int oldestTime;

	oldest = 0;
	oldestTime = 0x7fffffff;

	if( sv_showChallenge->integer )
		Com_Printf( "Challenge Packet %s\n", NET_AddressToString( address ) );

	// see if we already have a challenge for this ip
	for( i = 0; i < MAX_CHALLENGES; i++ )
	{
		if( NET_CompareBaseAddress( address, &svs.challenges[i].adr ) )
			break;
		if( svs.challenges[i].time < oldestTime )
		{
			oldestTime = svs.challenges[i].time;
			oldest = i;
		}
	}

	if( i == MAX_CHALLENGES )
	{
		// overwrite the oldest
		svs.challenges[oldest].challenge = rand() & 0x7fff;
		svs.challenges[oldest].adr = *address;
		svs.challenges[oldest].time = Sys_Milliseconds();
		i = oldest;
	}

	Netchan_OutOfBandPrint( socket, address, "challenge %i", svs.challenges[i].challenge );
}
Пример #2
0
/*
* TV_Downstream_GetChallenge
* 
* Returns a challenge number that can be used
* in a subsequent client_connect command.
* We do this to prevent denial of service attacks that
* flood the server with invalid upstream IPs.  With a
* challenge, they must give a valid IP address.
*/
static void TV_Downstream_GetChallenge( const socket_t *socket, const netadr_t *address )
{
	int i;
	int oldest;
	int oldestTime;

	oldest = 0;
	oldestTime = 0x7fffffff;

	// see if we already have a challenge for this ip
	for( i = 0; i < MAX_CHALLENGES; i++ )
	{
		if( NET_CompareBaseAddress( address, &tvs.challenges[i].adr ) )
			break;
		if( tvs.challenges[i].time < oldestTime )
		{
			oldestTime = tvs.challenges[i].time;
			oldest = i;
		}
	}

	if( i == MAX_CHALLENGES )
	{
		// overwrite the oldest
		tvs.challenges[oldest].challenge = rand() & 0x7fff;
		tvs.challenges[oldest].adr = *address;
		tvs.challenges[oldest].time = tvs.realtime;
		i = oldest;
	}

	Netchan_OutOfBandPrint( socket, address, "challenge %i", tvs.challenges[i].challenge );
}
Пример #3
0
static struct sockaddr_in *QRY_FL_Filtered(struct sockaddr_in *addr)
{
	int						i;

	for (i = 0; i < server_filter.count; i++)
	{
		if (NET_CompareBaseAddress(addr, &server_filter.addr[i]))
			return &server_filter.addr[i];
	}

	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
/*
* SV_ReadPackets
*/
static void SV_ReadPackets( void )
{
	int i, socketind, ret;
	client_t *cl;
#ifdef TCP_SUPPORT
	socket_t newsocket;
	netadr_t mmserver;
#endif
	int game_port;
	socket_t *socket;
	netadr_t address;

	static msg_t msg;
	static qbyte msgData[MAX_MSGLEN];

	socket_t* sockets [] =
	{
		&svs.socket_loopback,
		&svs.socket_udp,
		&svs.socket_udp6,
	};

#ifdef TCP_SUPPORT

	if( SV_MM_Initialized() ) 
		SV_MM_NetAddress( &mmserver );

	if( svs.socket_tcp.open )
	{
		while( qtrue )
		{
			// find a free slot
			for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ )
			{
				if( !svs.incoming[i].active )
					break;
			}
			if( i == MAX_INCOMING_CONNECTIONS )
				break;

			if( ( ret = NET_Accept( &svs.socket_tcp, &newsocket, &address ) ) == 0 )
				break;
			if( ret == -1 )
			{
				Com_Printf( "NET_Accept: Error: %s\n", NET_ErrorString() );
				continue;
			}

			Com_Printf( "New TCP connection from %s\n", NET_AddressToString( &address ) );

			svs.incoming[i].active = qtrue;
			svs.incoming[i].socket = newsocket;
			svs.incoming[i].address = address;
			svs.incoming[i].time = svs.realtime;
		}
	}

	for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ )
	{
		if( !svs.incoming[i].active )
			continue;

		ret = NET_GetPacket( &svs.incoming[i].socket, &address, &msg );
		if( ret == -1 )
		{
			Com_Printf( "NET_GetPacket: Error: %s\n", NET_ErrorString() );
			NET_CloseSocket( &svs.incoming[i].socket );
			svs.incoming[i].active = qfalse;
		}
		else if( ret == 1 )
		{
			if( SV_MM_Initialized() && NET_CompareBaseAddress( &mmserver, &address ) )
			{
				Com_DPrintf( "TCP packet from matchmaker\n" );
				SV_MM_SetConnection( &svs.incoming[i] );
				SV_MM_Packet( &msg );
				SV_MM_SetConnection( NULL );
				continue;
			}
			if( *(int *)msg.data != -1 )
			{
				Com_Printf( "Sequence packet without connection\n" );
				NET_CloseSocket( &svs.incoming[i].socket );
				svs.incoming[i].active = qfalse;
				continue;
			}

			Com_Printf( "Connectionless TCP packet from: %s\n", NET_AddressToString( &address ) );

			SV_ConnectionlessPacket( &svs.incoming[i].socket, &address, &msg );
		}
	}
#endif

	MSG_Init( &msg, msgData, sizeof( msgData ) );

	for( socketind = 0; socketind < sizeof( sockets ) / sizeof( sockets[0] ); socketind++ )
	{
		socket = sockets[socketind];

		if( !socket->open )
			continue;

		while( ( ret = NET_GetPacket( socket, &address, &msg ) ) != 0 )
		{
			if( ret == -1 )
			{
				Com_Printf( "NET_GetPacket: Error: %s\n", NET_ErrorString() );
				continue;
			}

			// check for connectionless packet (0xffffffff) first
			if( *(int *)msg.data == -1 )
			{
				SV_ConnectionlessPacket( socket, &address, &msg );
				continue;
			}

			// read the game port out of the message so we can fix up
			// stupid address translating routers
			MSG_BeginReading( &msg );
			MSG_ReadLong( &msg ); // sequence number
			MSG_ReadLong( &msg ); // sequence number
			game_port = MSG_ReadShort( &msg ) & 0xffff;
			// data follows

			// check for packets from connected clients
			for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
			{
				unsigned short addr_port;

				if( cl->state == CS_FREE || cl->state == CS_ZOMBIE )
					continue;
				if( cl->edict && ( cl->edict->r.svflags & SVF_FAKECLIENT ) )
					continue;
				if( !NET_CompareBaseAddress( &address, &cl->netchan.remoteAddress ) )
					continue;
				if( cl->netchan.game_port != game_port )
					continue;

				addr_port = NET_GetAddressPort( &address );
				if( NET_GetAddressPort( &cl->netchan.remoteAddress ) != addr_port )
				{
					Com_Printf( "SV_ReadPackets: fixing up a translated port\n" );
					NET_SetAddressPort( &cl->netchan.remoteAddress, addr_port );
				}

				if( SV_ProcessPacket( &cl->netchan, &msg ) ) // this is a valid, sequenced packet, so process it
				{
					cl->lastPacketReceivedTime = svs.realtime;
					SV_ParseClientMessage( cl, &msg );
				}
				break;
			}
		}
	}

	// handle clients with individual sockets
	for( i = 0; i < sv_maxclients->integer; i++ )
	{
		cl = &svs.clients[i];

		if( cl->state == CS_ZOMBIE || cl->state == CS_FREE )
			continue;

		if( !cl->individual_socket )
			continue;

		// not while, we only handle one packet per client at a time here
		if( ( ret = NET_GetPacket( cl->netchan.socket, &address, &msg ) ) != 0 )
		{
			if( ret == -1 )
			{
				Com_Printf( "Error receiving packet from %s: %s\n", NET_AddressToString( &cl->netchan.remoteAddress ),
					NET_ErrorString() );
				if( cl->reliable )
					SV_DropClient( cl, DROP_TYPE_GENERAL, "Error receiving packet: %s", NET_ErrorString() );
			}
			else
			{
				if( SV_ProcessPacket( &cl->netchan, &msg ) )
				{
					// this is a valid, sequenced packet, so process it
					cl->lastPacketReceivedTime = svs.realtime;
					SV_ParseClientMessage( cl, &msg );
				}
			}
		}
	}
}
Пример #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
/*
* TV_Downstream_ReadPackets
*/
void TV_Downstream_ReadPackets( void )
{
	int i, socketind, ret, game_port;
	client_t *cl;
#ifdef TCP_SUPPORT
	socket_t newsocket;
#endif
	socket_t *socket;
	netadr_t address;
	msg_t msg;
	qbyte msgData[MAX_MSGLEN];

	socket_t* sockets [] =
	{
		&tvs.socket_udp,
		&tvs.socket_udp6,
	};

#ifdef TCP_SUPPORT
	if( tvs.socket_tcp.open )
	{
		while( qtrue )
		{
			// find a free slot
			for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ )
			{
				if( !tvs.incoming[i].active )
					break;
			}
			if( i == MAX_INCOMING_CONNECTIONS )
				break;

			if( ( ret = NET_Accept( &tvs.socket_tcp, &newsocket, &address ) ) == 0 )
				break;
			if( ret == -1 )
			{
				Com_Printf( "NET_Accept: Error: %s\n", NET_ErrorString() );
				continue;
			}

			tvs.incoming[i].active = qtrue;
			tvs.incoming[i].socket = newsocket;
			tvs.incoming[i].address = address;
			tvs.incoming[i].time = tvs.realtime;
		}
	}

	for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ )
	{
		if( !tvs.incoming[i].active )
			continue;

		ret = NET_GetPacket( &tvs.incoming[i].socket, &address, &msg );
		if( ret == -1 )
		{
			NET_CloseSocket( &tvs.incoming[i].socket );
			tvs.incoming[i].active = qfalse;
		}
		else if( ret == 1 )
		{
			if( *(int *)msg.data != -1 )
			{                  // sequence packet without upstreams
				NET_CloseSocket( &tvs.incoming[i].socket );
				tvs.incoming[i].active = qfalse;
				continue;
			}

			TV_Downstream_UpstreamlessPacket( &tvs.incoming[i].socket, &address, &msg );
		}
	}
#endif

	MSG_Init( &msg, msgData, sizeof( msgData ) );

	for( socketind = 0; socketind < sizeof( sockets ) / sizeof( sockets[0] ); socketind++ )
	{
		socket = sockets[socketind];

		while( socket->open && ( ret = NET_GetPacket( socket, &address, &msg ) ) != 0 )
		{
			if( ret == -1 )
			{
				Com_Printf( "NET_GetPacket: Error: %s\n", NET_ErrorString() );
				continue;
			}

			// check for upstreamless packet (0xffffffff) first
			if( *(int *)msg.data == -1 )
			{
				TV_Downstream_UpstreamlessPacket( socket, &address, &msg );
				continue;
			}

			// read the game port out of the message so we can fix up
			// stupid address translating routers
			MSG_BeginReading( &msg );
			MSG_ReadLong( &msg ); // sequence number
			MSG_ReadLong( &msg ); // sequence number
			game_port = MSG_ReadShort( &msg ) & 0xffff;
			// data follows

			// check for packets from connected clients
			for( i = 0, cl = tvs.clients; i < tv_maxclients->integer; i++, cl++ )
			{
				unsigned short remoteaddr_port, addr_port;

				if( cl->state == CS_FREE || cl->state == CS_ZOMBIE )
					continue;
				if( !NET_CompareBaseAddress( &address, &cl->netchan.remoteAddress ) )
					continue;
				if( cl->netchan.game_port != game_port )
					continue;

				remoteaddr_port = NET_GetAddressPort( &cl->netchan.remoteAddress );
				addr_port = NET_GetAddressPort( &address );
				if( remoteaddr_port != addr_port )
				{
					Com_DPrintf( "%s" S_COLOR_WHITE ": Fixing up a translated port from %i to %i\n", cl->name,
						remoteaddr_port, addr_port );
					NET_SetAddressPort( &cl->netchan.remoteAddress, addr_port );
				}

				if( TV_Downstream_ProcessPacket( &cl->netchan, &msg ) )
				{                                           // this is a valid, sequenced packet, so process it
					cl->lastPacketReceivedTime = tvs.realtime;
					TV_Downstream_ParseClientMessage( cl, &msg );
				}
				break;
			}
		}
	}

	// handle clients with individual sockets
	for( i = 0; i < tv_maxclients->integer; i++ )
	{
		cl = &tvs.clients[i];

		if( cl->state == CS_ZOMBIE || cl->state == CS_FREE )
			continue;

		if( !cl->individual_socket )
			continue;

		// not while, we only handle one packet per client at a time here
		if( ( ret = NET_GetPacket( cl->netchan.socket, &address, &msg ) ) != 0 )
		{
			if( ret == -1 )
			{
				Com_Printf( "%s" S_COLOR_WHITE ": Error receiving packet: %s\n", cl->name, NET_ErrorString() );
				if( cl->reliable )
					TV_Downstream_DropClient( cl, DROP_TYPE_GENERAL, "Error receiving packet: %s", NET_ErrorString() );
			}
			else
			{
				if( *(int *)msg.data == -1 )
				{
					TV_Downstream_UpstreamlessPacket( cl->netchan.socket, &address, &msg );
				}
				else
				{
					if( TV_Downstream_ProcessPacket( &cl->netchan, &msg ) )
					{
						cl->lastPacketReceivedTime = tvs.realtime;
						TV_Downstream_ParseClientMessage( cl, &msg );
					}
				}
			}
		}
	}
}