Ejemplo n.º 1
0
query_status_t deal_with_ottdmaster_packet(struct qserver *server, char *rawpkt, int pktlen)
{
	unsigned num;
	server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
	server->server_name = MASTER;

	if(swap_short_from_little(rawpkt) != pktlen)
	{
		malformed_packet( server, "invalid packet length" );
		return PKT_ERROR;
	}
	if(rawpkt[2] != 7)
	{
		malformed_packet( server, "invalid packet type" );
		return PKT_ERROR;
	}

	if(rawpkt[3] != 1)
	{
		malformed_packet( server, "invalid packet version" );
		return PKT_ERROR;
	}

	num = swap_short_from_little(&rawpkt[4]);
	rawpkt += 6;
	pktlen -= 6;
	if( num && num*6 <= pktlen )
	{
		unsigned i;
		server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len + pktlen );
		memset(server->master_pkt + server->master_pkt_len, 0, pktlen );
		server->master_pkt_len += pktlen;
		for( i = 0; i < num * 6; i += 6 )
		{
			memcpy(&server->master_pkt[i], &rawpkt[i], 4);
			server->master_pkt[i+4] = rawpkt[i+5];
			server->master_pkt[i+5] = rawpkt[i+4];
		}
		server->n_servers += num;
	}
	else
	{
		malformed_packet( server, "invalid packet" );
		return PKT_ERROR;
	}

	bind_sockets();

	return DONE_AUTO;
}
Ejemplo n.º 2
0
query_status_t deal_with_ottd_packet(struct qserver *server, char *rawpkt, int pktlen)
{
	unsigned char *ptr = (unsigned char*)rawpkt;
	unsigned char *end = (unsigned char*)(rawpkt + pktlen);
	unsigned char type;
	char* str;
	char buf[32];
	unsigned ver;

	server->n_servers++;
	if ( server->server_name == NULL)
	{
		server->ping_total += time_delta( &packet_recv_time, &server->packet_time1);
		server->n_requests++;
	}
	else
	{
		gettimeofday( &server->packet_time1, NULL);
	}

	FAIL_IF(pktlen < 4 || swap_short_from_little(rawpkt) > pktlen, "invalid packet");

	type = ptr[2];
	ver = ptr[3];
	ptr += 4;

	debug(3, "len %hu type %hhu ver %hhu", swap_short_from_little(rawpkt), type, ver);

	FAIL_IF(ver != 4 && ver != 5, "only version 4 and 5 servers are supported");

	if(type == 1) // info packet
	{
		unsigned numgrf = *ptr;
		FAIL_IF(ptr + numgrf * 20 + 1 > end, "invalid newgrf number");
		ptr += numgrf * 20 + 1;

		snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
		add_rule(server, "date_days", buf, NO_FLAGS);
		ptr += 4;

		snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
		add_rule(server, "startdate_days", buf, NO_FLAGS);
		ptr += 4;

		FAIL_IF(ptr + 3 > end, "invalid packet");

		snprintf(buf, sizeof(buf), "%hhu", ptr[0]);
		add_rule(server, "maxcompanies", buf, NO_FLAGS);
		snprintf(buf, sizeof(buf), "%hhu", ptr[1]);
		add_rule(server, "numcompanies", buf, NO_FLAGS);
		server->max_spectators = ptr[2];
		ptr += 3;

		GET_STRING;
		server->server_name = strdup(str);

		GET_STRING;
		add_rule(server, "version", str, NO_FLAGS);

		FAIL_IF(ptr + 7 > end, "invalid packet");

		{
			static const char* langs[] = {
				"any",
				"English",
				"German",
				"French"
			};
			unsigned i = *ptr++;
			if(i > 3) i = 0;
			add_rule(server, "language", (char*)langs[i], NO_FLAGS);
		}

		add_rule(server, "password", *ptr++ ? "1" : "0", NO_FLAGS);

		server->max_players = *ptr++;
		server->num_players = *ptr++;
		server->num_spectators = *ptr++;

		GET_STRING;

		server->map_name = strdup(str);

		snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
		add_rule(server, "map_width", buf, NO_FLAGS);
		snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
		add_rule(server, "map_height", buf, NO_FLAGS);

		{
			static const char* sets[] = {
				"temperate",
				"arctic",
				"desert",
				"toyland"
			};
			unsigned i = *ptr++;
			if(i > 3) i = 0;
			add_rule(server, "map_set", (char*)sets[i], NO_FLAGS);
		}

		add_rule(server, "dedicated", *ptr++ ? "1" : "0", NO_FLAGS);
	}
	else if(type == 3) // player packet
	{
		unsigned i, j;
		INVALID_IF(ptr + 2 > end);

		server->num_players = *ptr++;

		for(i = 0; i < server->num_players; ++i)
		{
			unsigned long long lli;
			struct player* player;
			unsigned char nr;

			nr = *ptr++;

			debug(3, "player number %d", nr);
			player = add_player(server, i);
			FAIL_IF(!player, "can't allocate player");

			GET_STRING;
			player->name = strdup(str);
			debug(3, "name %s", str);
			player->frags = 0;

			INVALID_IF(ptr + 4 + 3*8 + 2 + 1 + 2*MAX_VEHICLE_TYPES + 2*MAX_STATION_TYPES > end);

			snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
			player_add_info(player, "startdate", buf, 0);
			ptr += 4;

			lli = swap_long_from_little(ptr+4);
			lli <<= 32;
			lli += swap_long_from_little(ptr);
			snprintf(buf, sizeof(buf), "%lld", lli);
			player_add_info(player, "value", buf, 0);
			ptr += 8;

			lli = swap_long_from_little(ptr+4);
			lli <<= 32;
			lli = swap_long_from_little(ptr);
			snprintf(buf, sizeof(buf), "%lld", lli);
			player_add_info(player, "money", buf, 0);
			ptr += 8;

			lli = swap_long_from_little(ptr+4);
			lli <<= 32;
			lli += swap_long_from_little(ptr);
			snprintf(buf, sizeof(buf), "%lld", lli);
			player_add_info(player, "income", buf, 0);
			ptr += 8;

			snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
			player_add_info(player, "performance", buf, 0);
			ptr += 2;

			player_add_info(player, "password", *ptr?"1":"0", 0);
			++ptr;

			for (j = 0; j < MAX_VEHICLE_TYPES; ++j)
			{
				snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
				player_add_info(player, (char*)vehicle_types[j], buf, 0);
				ptr += 2;
			}
			for (j = 0; j < MAX_STATION_TYPES; ++j)
			{
				snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
				player_add_info(player, (char*)station_types[j], buf, 0);
				ptr += 2;
			}
			
			if (ver != 5)
			{
				// connections
				while(ptr + 1 < end && *ptr)
				{
					++ptr;
					GET_STRING; // client name
					debug(3, "%s played by %s", str, player->name);
					GET_STRING; // id
					INVALID_IF(ptr + 4 > end);
					ptr += 4;
				}

				++ptr; // record terminated by zero byte
			}
		}

		// spectators
		while(ptr + 1 < end && *ptr)
		{
			++ptr;
			GET_STRING; // client name
			debug(3, "spectator %s", str);
			GET_STRING; // id
			INVALID_IF(ptr + 4 > end);
			ptr += 4;
		}
		++ptr; // record terminated by zero byte

		server->next_rule = NO_SERVER_RULES; // we're done
		server->next_player_info = server->num_players; // we're done
	}
	else
	{
		malformed_packet( server, "invalid type" );
		return PKT_ERROR;
	}

	server->retry1 = n_retries; // we're done with this packet, reset retry counter

	return DONE_AUTO;
}
Ejemplo n.º 3
0
static query_status_t _deal_with_doom3_packet( struct qserver *server, char *rawpkt, int pktlen, unsigned version )
{
	char *ptr = rawpkt;
	char *end = rawpkt + pktlen;
	int type = 0;
	int size = 0;
	int tail_size = 4;
	int viewers = 0;
	int tv = 0;
	unsigned num_players = 0;
	unsigned challenge = 0;
	unsigned protocolver = 0;
	char tmp[32];

	server->n_servers++;
	if ( server->server_name == NULL)
	{
		server->ping_total += time_delta( &packet_recv_time, &server->packet_time1);
	}
	else
	{
		gettimeofday( &server->packet_time1, NULL);
	}

	// Check if correct reply
	if ( pktlen < sizeof(doom3_inforesponse) +4 +4 +1 ||
		memcmp( doom3_inforesponse, ptr, sizeof(doom3_inforesponse)) != 0 )
	{
		malformed_packet(server, "too short or invalid response");
		return PKT_ERROR;
	}
	ptr += sizeof(doom3_inforesponse);

	if ( 5 == version )
	{
		// TaskID
		ptr += 4;
		// osmask + ranked
		tail_size++;
	}

	challenge = swap_long_from_little(ptr);
	ptr += 4;

	protocolver = swap_long_from_little(ptr);
	ptr += 4;

	snprintf(tmp, sizeof(tmp), "%u.%u", protocolver >> 16, protocolver & 0xFFFF);
	debug(2, "challenge: 0x%08X, protocol: %s (0x%X)", challenge, tmp, protocolver);

	server->protocol_version = protocolver;
	add_rule( server, "protocol", tmp, NO_FLAGS );


	if ( 5 == version )
	{
		// Size Long
		size = swap_long_from_little(ptr);
		debug( 2, "Size = %d", size );
		ptr += 4;
	}

// Commented out until we have a better way to specify the expected version
// This is due to prey demo requiring version 4 yet prey retail version 3
/*
	if( protocolver >> 16 != version )
	{
		malformed_packet(server, "protocol version %u, expected %u", protocolver >> 16, version );
		return PKT_ERROR;
	}
*/

	while ( ptr < end )
	{
		// server info:
		// name value pairs null seperated
		// empty name && value signifies the end of section
		char *key, *val;
		unsigned keylen, vallen;

		key = ptr;
		ptr = memchr(ptr, '\0', end-ptr);
		if ( !ptr )
		{
			malformed_packet( server, "no rule key" );
			return PKT_ERROR;
		}
		keylen = ptr - key;

		val = ++ptr;
		ptr = memchr(ptr, '\0', end-ptr);
		if ( !ptr )
		{
			malformed_packet( server, "no rule value" );
			return PKT_ERROR;
		}
		vallen = ptr - val;
		++ptr;

		if( keylen == 0 && vallen == 0 )
		{
			type = 1;
			break; // end
		}

		debug( 2, "key:%s=%s:", key, val);

		// Lets see what we've got
		if ( 0 == strcasecmp( key, "si_name" ) )
		{
			server->server_name = strdup( val );
		}
		else if( 0 == strcasecmp( key, "fs_game" ) )
		{
			server->game = strdup( val );
		}
#if 0
		else if( 0 == strcasecmp( key, "si_version" ) )
		{
			// format:
x
			// DOOM 1.0.1262.win-x86 Jul  8 2004 16:46:37
			server->protocol_version = atoi( val+1 );
		}
#endif
		else if( 0 == strcasecmp( key, "si_map" ) )
		{
			if ( 5 == version || 6 == version )
			{
				// ET:QW reports maps/<mapname>.entities
				// so we strip that here if it exists
				char *tmpp = strstr( val, ".entities" );
				if ( NULL != tmpp )
				{
					*tmpp = '\0';
				}

				if ( 0 == strncmp( val, "maps/", 5 ) )
				{
					val += 5;
				}
			}
			server->map_name = strdup( val );
		}
		else if ( 0 == strcasecmp( key, "si_maxplayers" ) )
		{
			server->max_players = atoi( val );
		}
		else if (  0 == strcasecmp( key, "ri_maxViewers" ) )
		{
			char max[20];
			sprintf( max, "%d", server->max_players );
			add_rule( server, "si_maxplayers", max, NO_FLAGS );
			server->max_players = atoi( val );
		}
		else if (  0 == strcasecmp( key, "ri_numViewers" ) )
		{
			viewers = atoi( val );
			tv = 1;
		}

		add_rule( server, key, val, NO_FLAGS );
	}

	if ( type != 1 )
	{
		// no more info should be player headers here as we
		// requested it
		malformed_packet( server, "player info missing" );
		return PKT_ERROR;
	}

	// now each player details
	while( ptr < end - tail_size )
	{
		struct player *player;
		char *val;
		unsigned char player_id = *ptr++;
		short ping = 0;
		unsigned rate = 0;

		if( ( 6 == version && MAX_WOLF_ASYNC_CLIENTS == player_id ) || MAX_DOOM3_ASYNC_CLIENTS == player_id )
		{
			break;
		}
		debug( 2, "ID = %d\n", player_id );

		// Note: id's are not steady
		if ( ptr + 7 > end ) // 2 ping + 4 rate + empty player name ('\0')
		{
			// run off the end and shouldnt have
			malformed_packet( server, "player info too short" );
			return PKT_ERROR;
		}
/*
		if ( 6 == version )
		{
			// Playerid is broken in wolf its always 0
			player_id = num_players;
		}
*/

		player = add_player( server, player_id );
		if( ! player )
		{
			malformed_packet( server, "duplicate player id" );
			return PKT_ERROR;
		}

		// doesnt support score so set a sensible default
		player->score = 0;
		player->frags = 0;

		// Ping
		ping = swap_short_from_little(ptr);
		player->ping = ping;
		ptr += 2;

		if ( 5 != version || 0xa0013 >= protocolver ) // No Rate in ETQW since v1.4 ( protocol v10.19 )
		{
			// Rate
			rate = swap_long_from_little(ptr);
			{
				char buf[16];
				snprintf(buf, sizeof(buf), "%u", rate);
				player_add_info(player, "rate", buf, NO_FLAGS);
			}
			ptr += 4;
		}

		if ( 5 == version && ( ( 0xd0009 == protocolver || 0xd000a == protocolver ) && 0 != num_players ) ) // v13.9 or v13.10
		{
			// Fix the packet offset due to the single bit used for bot
			// which realigns at the byte boundary for the player name
			ptr++;
		}

		// Name
		val = ptr;
		ptr = memchr(ptr, '\0', end-ptr);
		if ( !ptr )
		{
			malformed_packet( server, "player name not null terminated" );
			return PKT_ERROR;
		}
		player->name = strdup( val );
		ptr++;

		switch( version )
		{
		case 2: // Quake 4
			val = ptr;
			ptr = memchr(ptr, '\0', end-ptr);
			if ( !ptr )
			{
				malformed_packet( server, "player clan not null terminated" );
				return PKT_ERROR;
			}
			player->tribe_tag = strdup( val );
			ptr++;
			debug( 2, "Player[%d] = %s, ping %hu, rate %u, id %hhu, clan %s",
					num_players, player->name, ping, rate, player_id, player->tribe_tag);
			break;

		case 5: // ETQW
		case 6: // Wolfenstien
			if ( 0xa0011 <= protocolver ) // clan tag since 10.17
			{
				// clantag position
				ptr++;

				// clantag
				val = ptr;
				ptr = memchr(ptr, '\0', end-ptr);
				if ( !ptr )
				{
					malformed_packet( server, "player clan not null terminated" );
					return PKT_ERROR;
				}
				player->tribe_tag = strdup( val );
				ptr++;
			}

			// Bot flag
			if ( 0xd0009 == protocolver || 0xd000a == protocolver ) // v13.9 or v13.10
			{
				// Bot flag is a single bit so need to realign everything from here on in :(
				int i;
				unsigned char *tp = (unsigned char*)ptr;
				player->type_flag = (*tp)<<7;

				// alignment is reset at the end
				for( i = 0; i < 8 && tp < (unsigned char*)end; i++ )
				{
					*tp = (*tp)>>1 | *(tp+1)<<7;
					tp++;
				}
			}
			else
			{
				player->type_flag = *ptr++;
			}

			if ( 0xa0011 <= protocolver ) // clan tag since 10.17
			{
				debug( 2, "Player[%d] = %s, ping %hu, rate %u, id %hhu, bot %hu, clan %s",
					num_players, player->name, ping, rate, player_id, player->type_flag, player->tribe_tag);
			}
			else
			{
				debug( 2, "Player[%d] = %s, ping %hu, rate %u, id %hhu, bot %hu",
					num_players, player->name, ping, rate, player_id, player->type_flag );
			}
			break;

		default:
			debug( 2, "Player[%d] = %s, ping %hu, rate %u, id %hhu", num_players, player->name, ping, rate, player_id );
		}