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