示例#1
0
/**
 * @param in incoming metaserver packet
 *
 * 	NMT_SERVERKEEPALIVE		indicates a keep alive for a server, also serves as a "registration"
 * 	Response Packet Type: NMT_HANDSHAKE
 * 	- pack packet type
 * 	- pack random number
 */
void
MetaServer::processSERVERKEEPALIVE(const MetaServerPacket& in, MetaServerPacket& out)
{

	uint32_t i = msdo.addHandshake();

	if ( i > 0 )
	{
		m_Logger.debug("processSERVERKEEPALIVE(%u)", i);
		out.setPacketType(NMT_HANDSHAKE);
		out.addPacketData(i);
		out.setAddress( in.getAddress() );
	}
}
    /*
     * I would like to thank this particular integration test
     * for showing me that the setUp is called for every test,
     * not once per testsuite as I had originally thought.
     */
    void setUp()
    {
    	mp1 = new MetaServerPacket();
    	mp1->setPacketType(NMT_SERVERKEEPALIVE);
    	mp1->setSequence(1);
    	mp1->setTimeOffset(1);

    	mp2 = new MetaServerPacket();
    	mp2->setPacketType(NMT_SERVERSHAKE);
    	mp2->addPacketData(22);
    	mp2->setSequence(2);
    	mp2->setTimeOffset(2);

		p = new PacketReader();
		file_name = "/tmp/packet_test.bin";
		pl = new PacketLogger(file_name);

    }
int main(int argc, char** argv)
{

	/**
	 * Argument Wrangling
	 *
	 */
	boost::program_options::options_description desc( "TestClient" );
	boost::program_options::variables_map vm;
	boost::asio::io_service io_service;
	std::array<char, MAX_PACKET_BYTES> recvBuffer;
	boost::asio::ip::udp::endpoint sender_endpoint;
	size_t bytes_recvd;


	/**
	 * Note: options inside the configuration file that are NOT listed here
	 *       become ignored and are not accessible.
	 */
	desc.add_options()
		( "help,h", "Display help message" )
		( "server", boost::program_options::value<std::string>()->default_value("localhost"), "MetaServer host. \nDefault:localhost" )
		( "port", boost::program_options::value<int>()->default_value(8453), "MetaServer port. \nDefault:8453" )
		( "attribute", boost::program_options::value<attribute_list>(), "Set client attribute.\nDefault: none" )
		( "filter", boost::program_options::value<attribute_list>(), "Set client filters.\nDefault: none" )
		( "keepalives", boost::program_options::value<int>()->default_value(3), "Number of Keepalives. \nDefault:3" )
			;

	try
	{

		boost::program_options::store(
				boost::program_options::parse_command_line(argc, argv, desc),
				vm
				);
		boost::program_options::notify(vm);

		/**
		 * Special case for help
		 */
		if ( vm.count("help") )
		{
			std::cout << desc << std::endl;
			return 0;
		}

		std::cout << "Server       : " << vm["server"].as<std::string>() << std::endl;
		std::cout << "Port         : " << vm["port"].as<int>() << std::endl;
		std::cout << "Keepalives   : " << vm["keepalives"].as<int>() << std::endl;
		std::cout << "---------------" << std::endl;

		for (boost::program_options::variables_map::iterator it=vm.begin(); it!=vm.end(); ++it )
		{
			if ( it->second.value().type() == typeid(int) )
			{
				std::cout << it->first.c_str() << "=" << it->second.as<int>() << std::endl;
			}
			else if (it->second.value().type() == typeid(std::string) )
			{
				std::cout << it->first.c_str() << "=" << it->second.as<std::string>().c_str() << std::endl;
			}
			else if (it->second.value().type() == typeid(attribute_list) )
			{
				std::cout << it->first.c_str() << "=Attribute List" << std::endl;
			}
		}

		std::cout << "-------------------------" << std::endl;

		/**
		 * because boost query is too stupid to take port as an int
		 */
		std::stringstream port_str;
		port_str << vm["port"].as<int>();

		boost::asio::ip::udp::socket s(io_service, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0));
		boost::asio::ip::udp::resolver resolver(io_service);
		boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), vm["server"].as<std::string>(), port_str.str() );
		boost::asio::ip::udp::resolver::iterator iterator = resolver.resolve(query);

		/**
		 *  Step 1 : keepalive x3 w/ sleep
		 */

		/**
		 *    1.1 - send keepalive
		 */
		for ( int i=0 ; i < vm["keepalives"].as<int>() ; ++i )
		{
			MetaServerPacket keep;
			keep.setPacketType(NMT_CLIENTKEEPALIVE);

			std::cout << "Sending keepalive ... ";
			s.send_to(boost::asio::buffer(keep.getBuffer(), keep.getSize()), *iterator );

			/**
			 *    1.2 - receive handshake
			 */

			bytes_recvd = s.receive_from( boost::asio::buffer(recvBuffer), sender_endpoint );

			MetaServerPacket shake( recvBuffer, bytes_recvd );
			shake.setAddress(sender_endpoint.address().to_string());
			shake.setPort(sender_endpoint.port());
			std::cout << "Got handshake ... ";

			unsigned int shake_key = shake.getIntData(4);

			/**
			 *    1.3 - send clientshake
			 */
			MetaServerPacket clientshake;
			clientshake.setPacketType(NMT_CLIENTSHAKE);
			clientshake.addPacketData(shake_key);
			clientshake.setAddress( shake.getAddress() );
			s.send_to(boost::asio::buffer(clientshake.getBuffer(), clientshake.getSize()), *iterator );
			std::cout << "Sending registration." << std::endl;
			//std::cout << "Sleeping between keepalives : 2s" << std::endl;
			//sleep(2);
		}

		/**
		 *  Step 2 : register attributes if any
		 */
		if ( vm.count("attribute") )
		{
			std::cout << "Registering Client Attributes: " << std::endl;
			attribute_list v = vm["attribute"].as<attribute_list>();
			while(!v.empty())
			{
				std::string ele = v.back();
				size_t pos = ele.find_first_of("=");
				if( pos != std::string::npos )
				{
					std::string n = ele.substr(0,pos);
					std::string v = ele.substr(pos+1);
					std::cout << " register: " << n << std::endl;
					std::cout << "    value: " << v << std::endl;
					MetaServerPacket a;
					a.setPacketType(NMT_CLIENTATTR);
					a.addPacketData(n.length());
					a.addPacketData(v.length());
					a.addPacketData(n);
					a.addPacketData(v);
					s.send_to(boost::asio::buffer(a.getBuffer(), a.getSize()), *iterator );
				}
				else
				{
					std::cout << " Attribute Ignored : " << ele << std::endl;
				}
				v.pop_back();
			}
		}

		/**
		 *  Step 3 : register filters
		 */
		if ( vm.count("filter") )
		{
			std::cout << "Registering Client Filters: " << std::endl;
			attribute_list v = vm["filter"].as<attribute_list>();
			while(!v.empty())
			{
				std::string ele = v.back();
				size_t pos = ele.find_first_of("=");
				if( pos != std::string::npos )
				{
					std::string n = ele.substr(0,pos);
					std::string v = ele.substr(pos+1);
					std::cout << " register: " << n << std::endl;
					std::cout << "    value: " << v << std::endl;
					MetaServerPacket a;
					a.setPacketType(NMT_CLIENTFILTER);
					a.addPacketData(n.length());
					a.addPacketData(v.length());
					a.addPacketData(n);
					a.addPacketData(v);
					s.send_to(boost::asio::buffer(a.getBuffer(), a.getSize()), *iterator );
				}
				else
				{
					std::cout << " Filter Ignored : " << ele << std::endl;
				}
				v.pop_back();
			}
		}

		/**
		 *  Step 4 : send listreq
		 */
		unsigned int total=1;
		unsigned int from=0;
		unsigned int packed=0;
		unsigned int count=0;
		while(1)
		{

			if ( from > total || total == 0 )
				break;

			std::cout << "List Request: " << std::endl;
			MetaServerPacket req;

			req.setPacketType(NMT_LISTREQ);
			req.addPacketData(from);
			req.setAddress( sender_endpoint.address().to_string() );
			req.setPort( sender_endpoint.port() );
			s.send_to(boost::asio::buffer(req.getBuffer(), req.getSize()), *iterator );

			bytes_recvd = s.receive_from( boost::asio::buffer(recvBuffer), sender_endpoint );

			MetaServerPacket resp( recvBuffer, bytes_recvd );
			resp.setAddress(sender_endpoint.address().to_string());
			resp.setPort(sender_endpoint.port());

			if ( resp.getPacketType() != NMT_LISTRESP || resp.getPacketType() == NMT_PROTO_ERANGE )
				break;

			std::cout << "Received server list packet";
			total = resp.getIntData(sizeof(uint32_t)*1); // 4
			packed = resp.getIntData(sizeof(uint32_t)*2); // 8
			std::cout << "  Received " << packed << " / " << total << " servers." << std::endl;

			for ( count = 1 ; count <= packed; count++ )
			{
				unsigned int offset = (sizeof(uint32_t)*2) + (sizeof(uint32_t)*count);
				//std::cout << "     " << count << " / " << offset << " == ";
				uint32_t ip = resp.getIntData(offset);
				//std::cout << ip << std::endl;
				std::cout << "Server: " << resp.IpNetToAscii(ip) << std::endl;
			}
			from += packed;
		}

		/**
		 *  Step 4: send terminate
		 */
		std::cout << "Sending Terminate: " << std::endl;
		MetaServerPacket term;
		term.setPacketType(NMT_TERMINATE);
		term.addPacketData(0); // increase size of term packet to indicate client
		term.setAddress( sender_endpoint.address().to_string() );
		term.setPort( sender_endpoint.port() );
		s.send_to(boost::asio::buffer(term.getBuffer(), term.getSize()), *iterator );

	}
	catch (std::exception& e)
	{
		std::cerr << "Exception: " << e.what() << std::endl;
	}
	std::cout << "All Done!" << std::endl;
	return 0;
}
示例#4
0
/**
 * NMT_LISTREQ:
 * 		4 bytes type
 * 		4 bytes start index
 *
 * Response:
 *
 * NMT_LISTRESP
 *
 * 		4 bytes type
 * 		4 bytes total servers in list
 * 		4 bytes servers in this packet ( triggering client to have another REQ with total-servers offset )
 * 		4 bytes per server in this packet
 *
 */
void
MetaServer::processLISTREQ( const MetaServerPacket& in, MetaServerPacket& out)
{
	uint32_t server_index = in.getIntData(4);
	uint32_t total = msdo.getServerSessionCount();
	uint32_t packed_max = total;
	uint32_t packed = 0;
	std::list<uint32_t> resp_list;

	/*
	 * If we are unable to pack the entire list into 1 packet
	 */
	if ( (total*sizeof(uint32_t) - (server_index*sizeof(uint32_t)) ) > (MAX_UDP_OUT_BYTES-4-4-4) )
	{
		/*
		 * We want it to round ... just like the price is right, the goal is not to go over
		 */
		packed_max = (MAX_UDP_OUT_BYTES-4-4-4) / sizeof(uint32_t);
	}


	/*
	 * We hide the craziness of what goes on here inside the single method.
	 * The goal, is to get the list of servers constrained by packed_max
	 */
	std::list<std::string> sess_list = msdo.getServerSessionList(server_index,packed_max);
    std::list<std::string>::iterator list_itr;

    for ( list_itr = sess_list.begin(); list_itr != sess_list.end() ; list_itr++ )
    {
    	/*
    	 * Defensive to make sure we're not going to exceed our max
    	 */
    	if ( packed >= packed_max )
    		break;

    	/*
    	 * Defensive to make sure that the item in the list is
    	 * actually a valid data item we can send ( orthogonal processes
    	 * such as expiry could invalidate at any time ).
    	 *
    	 * Thus we can iterate over as much of the server list as we need to
    	 * and dead items won't count, only those added to the response packet.
    	 */
    	if ( msdo.serverSessionExists( *list_itr ))
    	{
    		/*
    		 * Note: see if there is a way to do this without atoi
    		 */
    		resp_list.push_back( atoi( msdo.getServerSession(*list_itr)["ip_int"].c_str() ) );
    		++packed;
    	}

    }

    if ( packed != resp_list.size() )
    {
    	m_Logger.warn("Packed (%u) vs Response(%u) MISMATCH!", packed, resp_list.size() );
    }

	out.setAddress( in.getAddress() );
	out.setPacketType(NMT_LISTRESP);

	/**
	 * If the list is empty, just send a 0,0 to indicate completion.
	 * NOTE: I think this logic is a bug in the protocol, as the
	 * 		original MS code looks as if this was just not working correctly.
	 */
	if ( resp_list.size() > 0 )
	{
		out.addPacketData( msdo.getServerSessionCount() );
		out.addPacketData( (uint32_t)resp_list.size() );
		while ( ! resp_list.empty() )
		{
			m_Logger.debug("processLISTRESP(%d) - Adding", resp_list.front() );
			out.addPacketData(resp_list.front());
			resp_list.pop_front();
		}
	}
	else
	{
		/*
		 *  For the record, I think this is a stupid protocol construct
		 */
		m_Logger.debug("processLISTRESP(0,0) - Empty");
		out.addPacketData( 0 );
		out.addPacketData( 0 );
	}

}