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