1 /*
2  * qstat
3  * by Steve Jankowski
4  *
5  * Teeworlds protocol
6  * Thanks to Emiliano Leporati for the first Teeworlds server patch (2008)
7  * Thanks to Thomas Debesse for the server rewrite and master addition <dev@illwieckz.net> (2014-2016)
8  * Thanks to Steven Hartland for some parts and generous help <steven.hartland@multiplay.co.uk> (2014)
9  *
10  * Licensed under the Artistic License, see LICENSE.txt for license terms
11  */
12 
13 #include <string.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 
17 #include "debug.h"
18 #include "qstat.h"
19 #include "packet_manip.h"
20 
21 /* See "scripts/tw_api.py" from Teeworlds project */
22 
23 /* query server */
24 static char teeserver_request_packet[15] = { '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', 'g', 'i', 'e', '\x00', '\x00' };
25 static int len_teeserver_request_packet = sizeof(teeserver_request_packet) / sizeof(char);
26 
27 /* server response */
28 static char teeserver_info_headerprefix[13] = { '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', 'i', 'n', 'f' };
29 static int len_teeserver_info_headerprefix = sizeof(teeserver_info_headerprefix) / sizeof(char);
30 
31 /*
32  * To request, we will try 3 request packets, only one character and the size differ, so no need to declare 3 strings
33  *
34  * char teeserver_request_packet[14] = { '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', 'g', 'i', 'e', 'f' };
35  * char teeserver_request_packe2[15] = { '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', 'g', 'i', 'e', '2', '\x00' };
36  * char teeserver_request_packe3[15] = { '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', 'g', 'i', 'e', '3', '\x00' };
37  *
38  * To analyze response, we will compare the same string without the last character, then the last character, so no need to declare 3 strings
39  *
40  * char teeserver_info_header[14] = { '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', 'i', 'n', 'f', 'o' };
41  * char teeserver_inf2_header[14] = { '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', 'i', 'n', 'f', '2' };
42  * char teeserver_inf3_header[14] = { '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', 'i', 'n', 'f', '3' };
43  */
44 
45 /*
46  * For information, Teeworlds packet samples per header, explained
47  *
48  * Header "info": \xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', 'i', 'n', 'f', 'o'
49  * Answer sample: '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffinfo0.5.1\x00.:{TeeKnight|Catch16}:. Catch16 hosted by TeeKnight.de\x00lightcatch\x00Catch16\x000\x00-1\x000\x0016\x00'
50  * Answer format: (*char)header,(*char)version,(*char)name,(*char)map,(*char)gametype,(int)flags,(int)progression,(int)num_players,(int)max_players,*players[]
51  *
52  * Header "inf2": \xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', 'i', 'n', 'f', '2'
53  * Answer sample: '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffinf20\x000.5.1\x00.:{TeeKnight|Catch16}:. Catch16 hosted by TeeKnight.de\x00lightcatch\x00Catch16\x000\x00-1\x000\x0016\x00'
54  * Answer format: (*char)header,(*char)token,(*char)version),(*char)name,(*char)map,(*char)gametype,(int)flags,(int)progression,(int)num_players,(int)max_players,*players[]
55  *
56  * Header "inf3": \xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', 'i', 'n', 'f', '3'
57  * Answer sample: '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffinf30\x000.6.1\x00[Bigstream.ru] CTF5 only\x00ctf5\x00CTF\x000\x000\x0016\x000\x0016\x00'
58  * Answer format: (*char)header,(*char)token,(*char)version),(*char)name,(*char)map,(*char)gametype,(int)flags,(int)num_players,(int)max_players,(int)num_clients,(int)max_clients,*players[]
59  */
60 
61 /* query master */
62 static char teemaster_packet[14] = { '\x20', '\x00', '\x00', '\x00', '\x00', '\x00', '\xFF', '\xFF', '\xFF', '\xFF', 'r', 'e', 'q', 't' };
63 static int len_teemaster_packet = sizeof(teemaster_packet) / sizeof(char);
64 
65 /* master response header, the last char defines the protocol version, everything before that last char is the same */
66 static char teemaster_list_headerprefix[13] = { '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', 'l', 'i', 's' };
67 static int len_teemaster_list_headerprefix = sizeof(teemaster_list_headerprefix) / sizeof(char);
68 static int len_teemaster_list_header = 1 + sizeof(teemaster_list_headerprefix) / sizeof(char);
69 
70 /*
71  * To request, we will try 2 request packets, only one character differs, so no need to declare 2 strings
72  *
73  * char teemaster_packet[14] = { '\x20', '\x00', '\x00', '\x00', '\x00', '\x00', '\xFF', '\xFF', '\xFF', '\xFF', 'r', 'e', 'q', 't' };
74  * char teemaster_packe2[14] = { '\x20', '\x00', '\x00', '\x00', '\x00', '\x00', '\xFF', '\xFF', '\xFF', '\xFF', 'r', 'e', 'q', '2' };
75  *
76  * To analyze response, we will compare the same string without the last character, so no need to declare 3 strings
77  *
78  * char teemaster_list_header[14] = { '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', 'l', 'i', 's', 't' };
79  * char teemaster_lis2_header[14] = { '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', 'l', 'i', 's', '2' };
80  */
81 
82 
83 /* ipv4 header for ipv4 addresses stored in ipv6 slots in normal server list */
84 static char tee_ipv4_header[12] = { '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xFF', '\xFF' };
85 static int len_tee_ipv4_header = sizeof(tee_ipv4_header) / sizeof(char);
86 
87 query_status_t
send_teeserver_request_packet(struct qserver * server)88 send_teeserver_request_packet(struct qserver *server)
89 {
90 	debug(2, "send_teeserver_request_packet %p", server);
91 
92 	query_status_t ret;
93 
94 	/*
95 	 * Try first then second then third...
96 	 * In fact the master server said which server use which protocol, but how qstat can transmit this information?
97 	 */
98 
99 	/* send getinfo1 packet (legacy server first query) */
100 	teeserver_request_packet[13] = 'f';
101 	ret = send_packet(server, teeserver_request_packet, 13);
102 	if (ret != INPROGRESS) {
103 		return (ret);
104 	}
105 
106 	/* send getinfo2 packet (legacy server second query for additional data) */
107 	teeserver_request_packet[13] = '2';
108 	ret = send_packet(server, teeserver_request_packet, len_teeserver_request_packet);
109 	if (ret != INPROGRESS) {
110 		return (ret);
111 	}
112 
113 	/* send getinfo3 packet (normal server lone query) */
114 	teeserver_request_packet[13] = '3';
115 	return (send_packet(server, teeserver_request_packet, len_teeserver_request_packet));
116 }
117 
118 
119 query_status_t
deal_with_teeserver_packet(struct qserver * server,char * rawpkt,int rawpktlen)120 deal_with_teeserver_packet(struct qserver *server, char *rawpkt, int rawpktlen)
121 {
122 	int i;
123 	char last_char;
124 	char *current = NULL, *end = NULL, *version = NULL, *tok = NULL;
125 	struct player *player;
126 
127 	debug(2, "deal_with_teeserver_packet %p, %d", server, rawpktlen);
128 
129 	server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
130 
131 	if (rawpktlen < len_teeserver_info_headerprefix) {
132 		malformed_packet(server, "packet too short");
133 		return (PKT_ERROR);
134 	}
135 
136 	/* not null-terminated packet */
137 	if ((strnlen(rawpkt, rawpktlen) == rawpktlen) && (rawpkt[rawpktlen] != 0)) {
138 		malformed_packet(server, "not null-terminated packet");
139 		return (PKT_ERROR);
140 	}
141 
142 	/* get the last character */
143 	last_char = rawpkt[len_teeserver_info_headerprefix];
144 
145 	/* compare the response without the last character */
146 	if (memcmp(rawpkt, teeserver_info_headerprefix, len_teeserver_info_headerprefix) != 0) {
147 		malformed_packet(server, "unknown packet header");
148 		return (PKT_ERROR);
149 	}
150 
151 	/* and verify if the last character is 'o', '2' or '3' */
152 	if ((last_char != 'o') && (last_char != '2') && (last_char != '3')) {
153 		malformed_packet(server, "unknown packet format");
154 		return (PKT_ERROR);
155 	}
156 
157 	current = rawpkt;
158 	end = rawpkt + rawpktlen;
159 
160 	/* header, skip */
161 	current += len_teeserver_info_headerprefix + sizeof(last_char);
162 
163 	/* if inf2 or inf3 */
164 	if ((last_char == '2') || (last_char == '3')) {
165 		/* token, skip */
166 		current += strnlen(current, end - current) + 1;
167 	}
168 
169 	/* version */
170 	version = current;
171 	current += strnlen(current, end - current) + 1;
172 
173 	/* server name */
174 	server->server_name = strdup(current);
175 	current += strnlen(current, end - current) + 1;
176 
177 	/* map name */
178 	server->map_name = strdup(current);
179 	current += strnlen(current, end - current) + 1;
180 
181 	/* game type */
182 	add_rule(server, server->type->game_rule, current, NO_FLAGS);
183 	current += strnlen(current, end - current) + 1;
184 
185 	/* flags, skip */
186 	current += strnlen(current, end - current) + 1;
187 
188 	/* if info or inf2 */
189 	if ((last_char == 'o') || (last_char == '2')) {
190 		// progression, skip
191 		current += strnlen(current, end - current) + 1;
192 	}
193 
194 	/* num players */
195 	server->num_players = atoi(current);
196 	current += strnlen(current, end - current) + 1;
197 
198 	/* max players */
199 	server->max_players = atoi(current);
200 	current += strnlen(current, end - current) + 1;
201 
202 	/* if inf3 */
203 	if (last_char == '3') {
204 		/* is there a difference between a Teeworlds spectator and what qstat calls a "client"? */
205 
206 		/* num clients, skip */
207 		current += strnlen(current, end - current) + 1;
208 
209 		/* max clients, skip */
210 		current += strnlen(current, end - current) + 1;
211 	}
212 
213 	/* players */
214 	for (i = 0; i < server->num_players; i++) {
215 		player = add_player(server, i);
216 		player->name = strdup(current);
217 		current += strnlen(current, end - current) + 1;
218 
219 		player->score = atoi(current);
220 		current += strnlen(current, end - current) + 1;
221 	}
222 
223 	/* version reprise */
224 	server->protocol_version = 0;
225 
226 	tok = strtok(version, ".");
227 	if (tok == NULL) {
228 		malformed_packet(server, "malformed server version string");
229 		return (PKT_ERROR);
230 	}
231 	server->protocol_version |= (atoi(tok) & 0x000F) << 12;
232 
233 	tok = strtok(NULL, ".");
234 	if (tok == NULL) {
235 		malformed_packet(server, "malformed server version string");
236 		return (PKT_ERROR);
237 	}
238 	server->protocol_version |= (atoi(tok) & 0x000F) << 8;
239 
240 	tok = strtok(NULL, ".");
241 	if (tok == NULL) {
242 		malformed_packet(server, "malformed server version string");
243 		return (PKT_ERROR);
244 	}
245 	server->protocol_version |= (atoi(tok) & 0x00FF);
246 
247 	return (DONE_FORCE);
248 }
249 
250 
251 query_status_t
send_teemaster_request_packet(struct qserver * server)252 send_teemaster_request_packet(struct qserver *server)
253 {
254 	debug(2, "send_teemaster_request_packet %p", server);
255 
256 	query_status_t ret_packet, ret_packe2;
257 
258 	/* query for legacy list (ipv4 only list) of legacy servers (using legacy getinfo and getinfo2 queries) */
259 	teemaster_packet[13] = 't';
260 	ret_packet = send_packet(server, teemaster_packet, len_teemaster_packet);
261 
262 	if (ret_packet != INPROGRESS) {
263 		return (ret_packet);
264 	}
265 
266 	/* query for normal list (mixed ipv4 and ipv6 list) of normal servers (using normal getinfo3 query) */
267 	teemaster_packet[13] = '2';
268 	ret_packe2 = send_packet(server, teemaster_packet, len_teemaster_packet);
269 
270 	return (ret_packe2);
271 }
272 
273 query_status_t
process_legacy_teemaster_packet_data(struct qserver * server,char * rawpkt,int rawpktlen)274 process_legacy_teemaster_packet_data(struct qserver *server, char *rawpkt, int rawpktlen)
275 {
276 	int num_servers, previous_len, len_address_packet, i;
277 	char *current, *dumb_pointer;
278 
279 	/* six bytes address */
280 	debug(1, "teeworlds master response uses legacy packet format (ipv4 server list)");
281 
282 	server->server_name = MASTER;
283 	/* length of an address packet in rawpktlen with legacy packet format */
284 	len_address_packet = 6;
285 
286 	num_servers = rawpktlen / len_address_packet;
287 	server->n_servers += num_servers;
288 
289 	previous_len = server->master_pkt_len;
290 	server->master_pkt_len = server->n_servers * 6;
291 	dumb_pointer = (char *)realloc(server->master_pkt, server->master_pkt_len);
292 	if (dumb_pointer == NULL) {
293 		free(server->master_pkt);
294 		debug(0, "Failed to realloc memory for internal master packet");
295 		return (MEM_ERROR);
296 	}
297 	server->master_pkt = dumb_pointer;
298 	current = server->master_pkt + previous_len;
299 
300 	for (i = 0; i < num_servers; i++) {
301 		if (rawpktlen < len_address_packet) {
302 			malformed_packet(server, "packet too short");
303 			return (PKT_ERROR);
304 		}
305 
306 		memcpy(current, rawpkt, 4);
307 		memcpy(current + 5, rawpkt + 4, 1);
308 		memcpy(current + 4, rawpkt + 5, 1);
309 		/* 6 is the internal length of an address in qstat's server->master_pkt */
310 		current += 6;
311 
312 		rawpkt += len_address_packet;
313 		rawpktlen -= len_address_packet;
314 	}
315 
316 	return (INPROGRESS);
317 }
318 
319 query_status_t
process_new_teemaster_packet_data(struct qserver * server,char * rawpkt,int rawpktlen)320 process_new_teemaster_packet_data(struct qserver *server, char *rawpkt, int rawpktlen)
321 {
322 	int num_servers, previous_len, len_address_packet, i;
323 	char *current, *dumb_pointer;
324 
325 	/* eighteen bytes address */
326 	debug(1, "teeworlds master response uses normal packet format (mixed ipv4/ipv6 server list)");
327 
328 	server->server_name = MASTER;
329 	len_address_packet = 18;
330 
331 	num_servers = rawpktlen / len_address_packet;
332 
333 	for (i = 0; i < num_servers; i++) {
334 		if (rawpktlen < len_address_packet) {
335 			malformed_packet(server, "packet too short");
336 			return (PKT_ERROR);
337 		}
338 
339 		if (memcmp(rawpkt, tee_ipv4_header, len_tee_ipv4_header) == 0) {
340 			debug(3, "teeworlds ipv4 server found in normal teeworlds master packet");
341 
342 			previous_len = server->master_pkt_len;
343 			server->n_servers += 1;
344 			server->master_pkt_len += 6;
345 			dumb_pointer = (char *)realloc(server->master_pkt, server->master_pkt_len);
346 			if (dumb_pointer == NULL) {
347 				free(server->master_pkt);
348 				debug(0, "Failed to realloc memory for internal master packet");
349 				return (MEM_ERROR);
350 			}
351 			server->master_pkt = dumb_pointer;
352 			current = server->master_pkt + previous_len;
353 
354 			memcpy(current, rawpkt + len_tee_ipv4_header, 6);
355 
356 		} else {
357 			/* currently unsuported */
358 			debug(3, "teeworlds ipv6 server found in normal teeworlds master packet, discarding");
359 		}
360 
361 		rawpkt += len_address_packet;
362 		rawpktlen -= len_address_packet;
363 	}
364 
365 	return (INPROGRESS);
366 }
367 
368 query_status_t
deal_with_teemaster_packet(struct qserver * server,char * rawpkt,int rawpktlen)369 deal_with_teemaster_packet(struct qserver *server, char *rawpkt, int rawpktlen)
370 {
371 	char last_char;
372 
373 	debug(2, "deal_with_teemaster_packet %p, %d", server, rawpktlen);
374 
375 	server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
376 
377 	if (rawpktlen < len_teemaster_list_headerprefix) {
378 		malformed_packet(server, "packet too short");
379 		return (PKT_ERROR);
380 	}
381 
382 	/* compare the response without the last character */
383 	if (memcmp(rawpkt, teemaster_list_headerprefix, len_teemaster_list_headerprefix) != 0) {
384 		malformed_packet(server, "unknown packet header");
385 		return (PKT_ERROR);
386 	}
387 
388 	/* get the last header character */
389 	last_char = rawpkt[len_teemaster_list_headerprefix];
390 
391 	/* jump to the data */
392 	rawpkt += len_teemaster_list_header;
393 	rawpktlen -= len_teemaster_list_header;
394 
395 	if (last_char == 't') {
396 		return process_legacy_teemaster_packet_data(server, rawpkt, rawpktlen);
397 	} else if (last_char == '2') {
398 		return process_new_teemaster_packet_data(server, rawpkt, rawpktlen);
399 	}
400 
401 	/* unknown server list format */
402 	malformed_packet(server, "unknown packet format");
403 	return (PKT_ERROR);
404 }
405