1 /*
2  * serverscanner/ServerScanner.cpp
3  *
4  * This file is part of Leges Motus, a networked, 2D shooter set in zero gravity.
5  *
6  * Copyright 2009-2010 Andrew Ayer, Nathan Partlan, Jeffrey Pfau
7  *
8  * Leges Motus is free and open source software.  You may redistribute it and/or
9  * modify it under the terms of version 2, or (at your option) version 3, of the
10  * GNU General Public License (GPL), as published by the Free Software Foundation.
11  *
12  * Leges Motus is distributed in the hope that it will be useful, but WITHOUT ANY
13  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
14  * PARTICULAR PURPOSE.  See the full text of the GNU General Public License for
15  * further detail.
16  *
17  * For a full copy of the GNU General Public License, please see the COPYING file
18  * in the root of the source code tree.  You may also retrieve a copy from
19  * <http://www.gnu.org/licenses/gpl-2.0.txt>, or request a copy by writing to the
20  * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21  * 02111-1307  USA
22  *
23  */
24 
25 #include "ServerScanner.hpp"
26 #include "ServerScannerNetwork.hpp"
27 #include "OutputGenerator.hpp"
28 #include "JsonGenerator.hpp"
29 #include "ReadableGenerator.hpp"
30 #include "common/PacketReader.hpp"
31 #include "common/PacketWriter.hpp"
32 #include "common/network.hpp"
33 #include "common/misc.hpp"
34 #include "common/timer.hpp"
35 
36 #include <vector>
37 #include <algorithm>
38 #include <cstdlib>
39 #include <cstring>
40 #include <ctime>
41 #include <cstring>
42 #include <iostream>
43 #include <fstream>
44 #include <limits>
45 #include <sstream>
46 #include <map>
47 
48 using namespace LM;
49 using namespace std;
50 
ServerScanner(const char * metaserver_address)51 ServerScanner::ServerScanner(const char* metaserver_address) : m_client_compat(COMPAT_VERSION), m_network(*this) {
52 	m_client_version = LM_VERSION;
53 	m_protocol_number = PROTOCOL_VERSION;
54 
55 	bool	success = false;
56 
57 	if (metaserver_address != NULL || (metaserver_address = getenv("LM_METASERVER")) != NULL) {
58 		// Address passed manually--override all else
59 		if (strchr(metaserver_address, ':') == NULL) {
60 			success = resolve_hostname(m_metaserver_address, metaserver_address, METASERVER_PORTNO);
61 		} else {
62 			success = resolve_hostname(m_metaserver_address, metaserver_address);
63 		}
64 	} else {
65 		metaserver_address = METASERVER_HOSTNAME;
66 		success = resolve_hostname(m_metaserver_address, METASERVER_HOSTNAME, METASERVER_PORTNO);
67 	}
68 
69 	if (!success) {
70 		cerr << "Unable to resolve metaserver hostname, `" << metaserver_address << "'. Metaserver scanning disabled." << endl;
71 	}
72 }
73 
scan(ostream * outfile,OutputType outtype,int to_scan)74 void 	ServerScanner::scan(ostream* outfile, OutputType outtype, int to_scan) {
75 	m_start_ticks = get_ticks();
76 	m_output = outfile;
77 
78 	srand(time(NULL));
79 	m_current_scan_id = rand();
80 
81 	if (outtype == OUTPUT_HUMAN_READABLE) {
82 		(*m_output) << "Scanning for servers compatible with version " << m_client_version << "..." << endl;
83 	}
84 
85 	if (to_scan & SCAN_METASERVER) {
86 		scan_metaserver();
87 	}
88 
89 	if (to_scan & SCAN_LOCAL_NETWORK) {
90 		scan_local_network();
91 	}
92 
93 	if (to_scan & SCAN_LOOPBACK) {
94 		scan_loopback();
95 	}
96 
97 	if (to_scan & SCAN_UPGRADE) {
98 		check_for_upgrade();
99 	}
100 
101 	// Receive packets from all servers
102 	// TODO customizable delay
103 	while (m_network.receive_packets(5000));
104 	output_results(outtype);
105 }
106 
server_info(const IPAddress & server_address,PacketReader & info_packet)107 void	ServerScanner::server_info(const IPAddress& server_address, PacketReader& info_packet) {
108 	uint32_t	request_packet_id;
109 	uint64_t	scan_start_time;
110 	info_packet >> request_packet_id >> scan_start_time;
111 
112 	if (request_packet_id != m_current_scan_id) {
113 		// From an old scan - ignore it
114 		return;
115 	}
116 
117 	if (server_address == m_metaserver_address) {
118 		// A response from the meta server
119 		// Now send an info packet to the server specified in this packet, to measure ping time and get the most up-to-date information
120 		IPAddress	server_address;
121 		info_packet >> server_address;
122 		scan_server(server_address);
123 	} else {
124 		// A response from an actual server
125 		// Get the info on the server, and present it to the user
126 		int server_protocol_version;
127 		Version server_compat_version;
128 		ServerList::Server info;
129 		info_packet >> server_protocol_version >> server_compat_version;
130 		info_packet >> info.current_map_name >> info.team_count[0] >> info.team_count[1] >>
131 			info.max_players >> info.uptime >> info.time_left_in_game >>
132 			info.server_name >> info.server_location;
133 
134 		info.ping = get_ticks() - scan_start_time;
135 
136 		//cerr << "Received INFO packet from " << format_ip_address(server_address, true) << ": Protocol=" << server_protocol_version << "; Compat version=" << server_compat_version << "; Map=" << info.current_map_name << "; Blue players=" << info.team_count[0] << "; Red players=" << info.team_count[1] << "; Ping time=" << get_ticks() - scan_start_time << "ms"  << "; Uptime=" << info.uptime << endl;
137 
138 		if (server_protocol_version != m_protocol_number || server_compat_version != m_client_compat) {
139 			//cerr << server_protocol_version << " != " << m_protocol_number << " || " << server_compat_version << " != " << m_client_compat << endl;
140 			// Different protocol version. Discard.
141 			return;
142 		}
143 
144 		m_server_list.add(server_address, info);
145 	}
146 }
147 
148 
hole_punch_packet(const IPAddress & server_address,PacketReader & packet)149 void	ServerScanner::hole_punch_packet(const IPAddress& server_address, PacketReader& packet) {
150 	uint32_t	scan_id;
151 	packet >> scan_id;
152 
153 	if (scan_id != m_current_scan_id) {
154 		return;
155 	}
156 
157 	scan_server(server_address);
158 }
159 
160 
upgrade_available(const IPAddress & server_address,PacketReader & packet)161 void	ServerScanner::upgrade_available(const IPAddress& server_address, PacketReader& packet) {
162 	string		latest_version;
163 	packet >> latest_version;
164 	ostringstream message;
165 
166 	cerr << "Your version of lmscan is out of date." << endl;
167 	cerr << "The latest version is " << latest_version << endl;
168 }
169 
output_results(OutputType type)170 void	ServerScanner::output_results(OutputType type) {
171 	uint64_t final = get_ticks() - m_start_ticks;
172 	switch (type) {
173 	case OUTPUT_HUMAN_READABLE: {
174 		ReadableGenerator out(m_output);
175 		m_server_list.output(&out, final);
176 	} break;
177 	case OUTPUT_JSON: {
178 		JsonGenerator out(m_output);
179 		m_server_list.output(&out, final);
180 	} break;
181 	}
182 }
183 
scan_loopback()184 void	ServerScanner::scan_loopback() {
185 	PacketWriter info_request_packet(INFO_PACKET);
186 	info_request_packet << m_protocol_number << m_current_scan_id << get_ticks() << m_client_version;
187 	IPAddress localhostip;
188 	if (resolve_hostname(localhostip, "localhost", DEFAULT_PORTNO)) {
189 		m_network.send_packet_to(localhostip, info_request_packet);
190 	}
191 }
192 
check_for_upgrade()193 void    ServerScanner::check_for_upgrade() {
194 	PacketWriter packet(UPGRADE_AVAILABLE_PACKET);
195 	packet << m_client_version;
196 	m_network.send_packet_to(m_metaserver_address, packet);
197 }
198 
scan_local_network()199 void	ServerScanner::scan_local_network() {
200 	PacketWriter info_request_packet(INFO_PACKET);
201 	info_request_packet << m_protocol_number << m_current_scan_id << get_ticks();
202 	m_network.broadcast_packet(DEFAULT_PORTNO, info_request_packet);
203 }
204 
scan_metaserver()205 void	ServerScanner::scan_metaserver() {
206 	if (m_metaserver_address.port != 0) {
207 		PacketWriter info_request_packet(INFO_PACKET);
208 		info_request_packet << m_protocol_number << m_current_scan_id << get_ticks() << m_client_version;
209 		m_network.send_packet_to(m_metaserver_address, info_request_packet);
210 	}
211 }
212 
scan_server(const IPAddress & server_address)213 void	ServerScanner::scan_server(const IPAddress& server_address) {
214 	PacketWriter info_request_packet(INFO_PACKET);
215 	info_request_packet << m_protocol_number << m_current_scan_id << get_ticks();
216 	m_network.send_packet_to(server_address, info_request_packet);
217 }
218