1 /*
2  * qstat
3  * by Steve Jankowski
4  *
5  * Cube 2 / Sauerbraten protocol
6  * Copyright 2011 NoisyB
7  *
8  * Licensed under the Artistic License, see LICENSE.txt for license terms
9  */
10 #include <string.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 
14 #include "debug.h"
15 #include "qstat.h"
16 #include "packet_manip.h"
17 
18 struct offset {
19 	unsigned char *d;
20 	int pos;
21 	int len;
22 };
23 
24 //#define SB_MASTER_SERVER "http://sauerbraten.org/masterserver/retrieve.do?item=list"
25 #define SB_PROTOCOL	258
26 #define MIN(a, b)    ((a) < (b) ? (a) : (b))
27 #define MAX_ATTR	255
28 #define MAX_STRING	1024
29 
30 static int
getint(struct offset * d)31 getint(struct offset *d)
32 {
33 	int val = 0;
34 
35 	if (d->pos >= d->len) {
36 		return (0);
37 	}
38 
39 	val = d->d[d->pos++] & 0xff;// 8 bit value
40 
41 	// except...
42 	if ((val == 0x80) && (d->pos < d->len - 2)) {       // 16 bit value
43 		val = (d->d[d->pos++] & 0xff);
44 		val |= (d->d[d->pos++] & 0xff) << 8;
45 	} else if ((val == 0x81) && (d->pos < d->len - 4)) {        // 32 bit value
46 		val = (d->d[d->pos++] & 0xff);
47 		val |= (d->d[d->pos++] & 0xff) << 8;
48 		val |= (d->d[d->pos++] & 0xff) << 16;
49 		val |= (d->d[d->pos++] & 0xff) << 24;
50 	}
51 
52 	return (val);
53 }
54 
55 
56 static char *
getstr(char * dest,int dest_len,struct offset * d)57 getstr(char *dest, int dest_len, struct offset *d)
58 {
59 	int len = 0;
60 
61 	if (d->pos >= d->len) {
62 		return (NULL);
63 	}
64 
65 	len = MIN(dest_len, d->len - d->pos);
66 	strncpy(dest, (const char *)d->d + d->pos, len)[len - 1] = 0;
67 	d->pos += strlen(dest) + 1;
68 
69 	return (dest);
70 }
71 
72 
73 static char *
sb_getversion_s(int n)74 sb_getversion_s(int n)
75 {
76 	static char *version_s[] =
77 	{
78 		"Justice",
79 		"CTF",
80 		"Assassin",
81 		"Summer",
82 		"Spring",
83 		"Gui",
84 		"Water",
85 		"Normalmap",
86 		"Sp",
87 		"Occlusion",
88 		"Shader",
89 		"Physics",
90 		"Mp",
91 		"",
92 		"Agc",
93 		"Quakecon",
94 		"Independence"
95 	};
96 
97 	n = SB_PROTOCOL - n;
98 	if ((n >= 0) && ((size_t)n < sizeof(version_s) / sizeof(version_s[0]))) {
99 		return (version_s[n]);
100 	}
101 	return ("unknown");
102 }
103 
104 
105 static char *
sb_getmode_s(int n)106 sb_getmode_s(int n)
107 {
108 	static char *mode_s[] =
109 	{
110 		"slowmo SP",
111 		"slowmo DMSP",
112 		"demo",
113 		"SP",
114 		"DMSP",
115 		"ffa/default",
116 		"coopedit",
117 		"ffa/duel",
118 		"teamplay",
119 		"instagib",
120 		"instagib team",
121 		"efficiency",
122 		"efficiency team",
123 		"insta arena",
124 		"insta clan arena",
125 		"tactics arena",
126 		"tactics clan arena",
127 		"capture",
128 		"insta capture",
129 		"regen capture",
130 		"assassin",
131 		"insta assassin",
132 		"ctf",
133 		"insta ctf"
134 	};
135 
136 	n += 6;
137 	if ((n >= 0) && ((size_t)n < sizeof(mode_s) / sizeof(mode_s[0]))) {
138 		return (mode_s[n]);
139 	}
140 	return ("unknown");
141 }
142 
143 
144 query_status_t
send_cube2_request_packet(struct qserver * server)145 send_cube2_request_packet(struct qserver *server)
146 {
147 	return (send_packet(server, server->type->status_packet, server->type->status_len));
148 }
149 
150 
151 query_status_t
deal_with_cube2_packet(struct qserver * server,char * rawpkt,int pktlen)152 deal_with_cube2_packet(struct qserver *server, char *rawpkt, int pktlen)
153 {
154 	// skip unimplemented ack, crc, etc
155 	int i;
156 	int numattr;
157 	int attr[MAX_ATTR];
158 	char buf[MAX_STRING];
159 
160 	enum {
161 		MM_OPEN = 0,
162 		MM_VETO,
163 		MM_LOCKED,
164 		MM_PRIVATE
165 	};
166 
167 	struct offset d;
168 	d.d = (unsigned char *)rawpkt;
169 	d.pos = 0;
170 	d.len = pktlen;
171 
172 	server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
173 	getint(&d);             // we have the ping already
174 	server->num_players = getint(&d);
175 	numattr = getint(&d);
176 	for (i = 0; i < numattr && i < MAX_ATTR; i++) {
177 		attr[i] = getint(&d);
178 	}
179 
180 	server->protocol_version = attr[0];
181 
182 	sprintf(buf, "%d %s", attr[0], sb_getversion_s(attr[0]));
183 	add_rule(server, "version", buf, NO_FLAGS);
184 
185 	sprintf(buf, "%d %s", attr[1], sb_getmode_s(attr[1]));
186 	add_rule(server, "mode", buf, NO_FLAGS);
187 
188 	sprintf(buf, "%d", attr[2]);
189 	add_rule(server, "seconds_left", buf, NO_FLAGS);
190 
191 	server->max_players = attr[3];
192 
193 	switch (attr[5]) {
194 	case MM_OPEN:
195 		sprintf(buf, "%d open", attr[5]);
196 		break;
197 
198 	case MM_VETO:
199 		sprintf(buf, "%d veto", attr[5]);
200 		break;
201 
202 	case MM_LOCKED:
203 		sprintf(buf, "%d locked", attr[5]);
204 		break;
205 
206 	case MM_PRIVATE:
207 		sprintf(buf, "%d private", attr[5]);
208 		break;
209 
210 	default:
211 		sprintf(buf, "%d unknown", attr[5]);
212 	}
213 	add_rule(server, "mm", buf, NO_FLAGS);
214 
215 	for (i = 0; i < numattr && i < MAX_ATTR; i++) {
216 		char buf2[MAX_STRING];
217 		sprintf(buf, "attr%d", i);
218 		sprintf(buf2, "%d", attr[i]);
219 		add_rule(server, buf, buf2, NO_FLAGS);
220 	}
221 
222 	getstr(buf, MAX_STRING, &d);
223 	server->map_name = strdup(buf);
224 	getstr(buf, MAX_STRING, &d);
225 	server->server_name = strdup(buf);
226 
227 	return (DONE_FORCE);
228 }
229