Beispiel #1
0
/**
 * NMT_SERVERSHAKE - last part of the 3-way server handshake
 * Response: None
 * @param in
 * @param out
 */
void
MetaServer::processSERVERSHAKE(const MetaServerPacket& in, MetaServerPacket& out)
{
	unsigned int shake = in.getIntData(4);
	std::string ip = in.getAddressStr();
	m_Logger.debug("processSERVERSHAKE(%u)", shake);

	/**
	 * If a handshake exists, we can know the following:
	 * 1) the client is valid
	 * 2) they have a non-expired handshake
	 *
	 * What we do then is to:
	 * 1) register a server session
	 * 2) de-register the handshake ( maybe we just let it expire ? )
	 */
	if( msdo.handshakeExists(shake) )
	{
		std::stringstream ss;
		ss << in.getPort();

		msdo.addServerSession(ip);
		msdo.addServerAttribute(ip,"port", ss.str() );

		// clear stream first
		ss.str("");
		ss << in.getAddressInt();

		msdo.addServerAttribute(ip,"ip_int", ss.str() );
	}

}
Beispiel #2
0
void
MetaServer::processCLIENTFILTER(const MetaServerPacket& in, MetaServerPacket& out)
{
	unsigned int name_length = in.getIntData(4);
	unsigned int value_length = in.getIntData(8);
	std::string msg = in.getPacketMessage(12);
	std::string name = msg.substr(0,name_length);
	std::string value = msg.substr(name_length);
	std::string ip = in.getAddressStr();
	m_Logger.debug("processCLIENTFILTER(%s,%s)", name.c_str(), value.c_str() );
	msdo.addClientFilter(ip,name,value);

	out.setPacketType(NMT_NULL);
}
Beispiel #3
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() );
	}
}
Beispiel #4
0
void
MetaServer::processCLIENTSHAKE(const MetaServerPacket& in, MetaServerPacket& out)
{
	unsigned int shake = in.getIntData(4);
	std::string ip = in.getAddressStr();
	m_Logger.debug("processCLIENTSHAKE(%u)", shake );

	if( msdo.handshakeExists(shake) )
	{
		std::stringstream ss;
		ss << in.getPort();

		msdo.addClientSession(ip);
		msdo.addClientAttribute(ip,"port", ss.str() );
	}

}
Beispiel #5
0
void
MetaServer::processTERMINATE(const MetaServerPacket& in, MetaServerPacket& out)
{

	/**
	 *  For backwards compat, we make a regular "TERM" packet end a server session
	 *  and a TERM packet with any additional data sent signifies a client session.
	 */
	if ( in.getSize() > (sizeof(uint32_t)) )
	{
		m_Logger.debug("processTERMINATE-client(%s)", in.getAddressStr().c_str() );
		msdo.removeClientSession(in.getAddressStr());
	}
	else
	{
		m_Logger.debug("processTERMINATE-server(%s)", in.getAddressStr().c_str() );
		msdo.removeServerSession(in.getAddressStr());
	}

}
    /*
     * 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( "PacketPlayer" );
	boost::program_options::variables_map vm;
	boost::asio::io_service io_service;
	//boost::array<char, MAX_PACKET_BYTES> recvBuffer;
	boost::asio::ip::udp::endpoint sender_endpoint;
	//size_t bytes_recvd;
	PacketReader* p;


	/**
	 * 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" )
		( "playonly", boost::program_options::value<std::string>(), "Play output info only.\nDefault: false" )
		( "file", boost::program_options::value<std::string>(), "Binary Packet logfile.\nDefault: none" )
			;

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

		/**
		 * Wrangle the file etc options
		 */
		std::string file = "";
		bool playOnly = false;

		if( vm.count("playonly") )
		{
			std::string s = vm["playonly"].as<std::string>();
			if ( boost::iequals(s,"true") )
			{
				playOnly = true;
			}
		}

		if( vm.count("file") )
		{
			file = vm["file"].as<std::string>();
		}

		if ( file.empty() )
		{
			std::cout << "--file option not specified, but is required" << std::endl;
			return 13;
		}


		/**
		 *	Initialize reader
		 */
		p = new PacketReader();
		int pkts = p->parseBinaryFile(file);

		if( playOnly )
		{
			while(p->hasPacket())
			{
				MetaServerPacket msp = p->pop();
				unsigned int i = msp.getPacketType();
				std::string s = msp.getOutBound() ? ">>>>" : "<<<<";
				std::cout << "Packet [";
				std::cout << std::setfill('0') << std::setw(10) << msp.getSequence();
				std::cout << "] " << s << " : " << NMT_PRETTY[i] << std::endl;
			}
		}
		else
		{
			std::cout << "TODO: play packets to listed metaserver" << std::endl;
		}

		std::cout << "Packet File : " << file << std::endl;
		std::cout << "Packet Count: " << pkts << std::endl;

	}
	catch (std::exception& e)
	{
		std::cerr << "Exception: " << e.what() << std::endl;
	}
	std::cout << "All Done!" << std::endl;
	return 0;
}
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;
}
Beispiel #9
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 );
	}

}
Beispiel #10
0
/**
 * Convenience method that evaluates what type of packet and call appropriate handle method
 * @param msp incoming metaserver packet
 * @param rsp outgoing metaserver packet to be filled
 */
void
MetaServer::processMetaserverPacket(MetaServerPacket& msp, MetaServerPacket& rsp)
{

	/*
	 * Packet Sequence: store this so that we can replay the packets in the
	 *                  same order after the fact
	 * Time Offset: time in milliseconds relative to the "start time".  The start time
	 *              is defined as the first packet to be processed.  I chose this because
	 *              it will be possible to replay the packets in the correct order, at
	 *              exactly the same rate, relative to the start of the first packet
	 */
	if ( m_PacketSequence == 0 )
		m_startTime = boost::posix_time::microsec_clock::local_time();

	++m_PacketSequence;
	msp.setSequence(m_PacketSequence);
	msp.setTimeOffset( getDeltaMillis() );

	switch(msp.getPacketType())
	{
	case NMT_SERVERKEEPALIVE:
		processSERVERKEEPALIVE(msp,rsp);
		break;
	case NMT_SERVERSHAKE:
		processSERVERSHAKE(msp,rsp);
		break;
	case NMT_TERMINATE:
		processTERMINATE(msp,rsp);
		break;
	case NMT_CLIENTKEEPALIVE:
		processCLIENTKEEPALIVE(msp,rsp);
		break;
	case NMT_CLIENTSHAKE:
		processCLIENTSHAKE(msp,rsp);
		break;
	case NMT_LISTREQ:
		processLISTREQ(msp,rsp);
		break;
	case NMT_SERVERATTR:
		processSERVERATTR(msp,rsp);
		break;
	case NMT_CLIENTATTR:
		processCLIENTATTR(msp,rsp);
		break;
	case NMT_CLIENTFILTER:
		processCLIENTFILTER(msp,rsp);
		break;
	default:
		--m_PacketSequence;
		m_Logger.debug("Packet Type [%u] not supported.", msp.getPacketType());
		break;
	}

	/*
	 * Flag response packets sequence and offset tagging
	 */
	++m_PacketSequence;
	rsp.setSequence(m_PacketSequence);
	rsp.setTimeOffset( getDeltaMillis() );

	/*
	 * Packet Logging
	 */
	if ( m_logPackets )
	{
		// always log the incoming packets, even if they are bad ( as a bad incoming packet could be the cause
		// of an issue )
		m_PacketLogger->LogPacket(msp);

		// we don't want to log if:
		// 1) sequence is 0 : this means the sequence is not set ... this means something has gone astray elsewhere
		// 2) packet type is NULL : these responses are never sent to the client
		if ( rsp.getSequence() != 0 && rsp.getPacketType() != NMT_NULL )
			m_PacketLogger->LogPacket(rsp);
	}

}