1 /* bzflag
2 * Copyright (c) 1993-2021 Tim Riker
3 *
4 * This package is free software; you can redistribute it and/or
5 * modify it under the terms of the license found in the file
6 * named COPYING that should have accompanied this file.
7 *
8 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 */
12
13 /* interface header */
14 #include "Ping.h"
15
16 /* system implementation headers */
17 #include <cstring>
18 #include <cmath>
19 #include <cctype>
20
21 /* common implementation headers */
22 #include "Protocol.h"
23 #include "TimeKeeper.h"
24 #include "multicast.h"
25
26 // incessant rebuilding for current versioning
27 #include "version.h"
28
29
30 //
31 // PingPacket
32 //
33
34 const int PingPacket::PacketSize = ServerIdPLen + 52;
35
PingPacket()36 PingPacket::PingPacket() : gameOptions(0), gameType(TeamFFA),
37 maxShots(1),
38 shakeWins(0),
39 shakeTimeout(0),
40 maxPlayerScore(0),
41 maxTeamScore(0),
42 maxTime(0),
43 maxPlayers(1),
44 rogueCount(0),
45 rogueMax(1),
46 redCount(0),
47 redMax(1),
48 greenCount(0),
49 greenMax(1),
50 blueCount(0),
51 blueMax(1),
52 purpleCount(0),
53 purpleMax(1),
54 observerCount(0),
55 observerMax(1)
56 {
57 // do nothing
58 }
59
~PingPacket()60 PingPacket::~PingPacket()
61 {
62 // do nothing
63 }
64
read(int fd,struct sockaddr_in * addr)65 bool PingPacket::read(int fd, struct sockaddr_in* addr)
66 {
67 char buffer[PacketSize], serverVersion[9];
68 uint16_t len, code;
69
70 // get packet
71 int n = recvBroadcast(fd, buffer, sizeof(buffer), addr);
72 if (n < 4)
73 return false;
74
75 // decode header
76 const void* buf = buffer;
77 buf = nboUnpackUShort(buf, len);
78 buf = nboUnpackUShort(buf, code);
79
80 // make sure we got the rest of the message
81 if (len != n - 4)
82 return false;
83
84 // check that it's a reply
85 if (code != MsgPingCodeReply)
86 return false;
87
88 // unpack body of reply
89 buf = unpack(buf, serverVersion);
90
91 // compare protocol version against my protocol version.
92 return (strncmp(serverVersion, getServerVersion(), 8) == 0);
93 }
94
waitForReply(int fd,const Address & from,int millisecondsToBlock)95 bool PingPacket::waitForReply(int fd,
96 const Address& from,
97 int millisecondsToBlock)
98 {
99 // block waiting on input. if the incoming message is not from the
100 // indicated source then ignore it and go back to waiting. if the
101 // incoming message is not a ping then ignore it and go back to waiting.
102 float blockTime = 0.0001f * (float)millisecondsToBlock;
103 TimeKeeper startTime = TimeKeeper::getCurrent();
104 TimeKeeper currentTime = startTime;
105 do
106 {
107 // prepare timeout
108 const float timeLeft = float(blockTime - (currentTime - startTime));
109 struct timeval timeout;
110 timeout.tv_sec = long(floorf(timeLeft));
111 timeout.tv_usec = long(1.0e+6f * (timeLeft - floorf(timeLeft)));
112
113 // wait for input
114 fd_set read_set;
115 FD_ZERO(&read_set);
116 FD_SET((unsigned int)fd, &read_set);
117 int nfound = select(fd+1, (fd_set*)&read_set, NULL, NULL, &timeout);
118
119 // if got a message read it. if a ping packet and from right
120 // sender then return success.
121 if (nfound < 0)
122 return false;
123 if (nfound > 0 && read(fd, NULL))
124 if (sourceAddr == from)
125 return true;
126
127 currentTime = TimeKeeper::getCurrent();
128 }
129 while (currentTime - startTime < blockTime);
130 return false;
131 }
132
write(int fd,const struct sockaddr_in * addr) const133 bool PingPacket::write(int fd,
134 const struct sockaddr_in* addr) const
135 {
136 char buffer[PacketSize] = {0};
137 void* buf = buffer;
138 buf = nboPackUShort(buf, PacketSize - 4);
139 buf = nboPackUShort(buf, MsgPingCodeReply);
140 buf = pack(buf, getServerVersion());
141 return sendBroadcast(fd, buffer, sizeof(buffer), addr) == sizeof(buffer);
142 }
143
isRequest(int fd,struct sockaddr_in * addr)144 bool PingPacket::isRequest(int fd,
145 struct sockaddr_in* addr)
146 {
147 if (fd < 0) return false;
148 char buffer[6];
149 const void *msg = buffer;
150 uint16_t len, code;
151 int size = recvBroadcast(fd, buffer, sizeof(buffer), addr);
152 if (size < 2) return false;
153 msg = nboUnpackUShort(msg, len);
154 msg = nboUnpackUShort(msg, code);
155 return code == MsgPingCodeRequest;
156 }
157
sendRequest(int fd,const struct sockaddr_in * addr)158 bool PingPacket::sendRequest(int fd,
159 const struct sockaddr_in* addr)
160 {
161 if (fd < 0 || !addr) return false;
162 char buffer[6];
163 void *msg = buffer;
164 msg = nboPackUShort(msg, 2);
165 msg = nboPackUShort(msg, MsgPingCodeRequest);
166 msg = nboPackUShort(msg, (uint16_t) 0);
167 return sendBroadcast(fd, buffer, sizeof(buffer), addr) == sizeof(buffer);
168 }
169
unpack(const void * buf,char * version)170 const void* PingPacket::unpack(const void* buf, char* version)
171 {
172 buf = nboUnpackString(buf, version, 8);
173 buf = serverId.unpack(buf);
174 buf = sourceAddr.unpack(buf);
175 buf = nboUnpackUShort(buf, gameType);
176 buf = nboUnpackUShort(buf, gameOptions);
177 buf = nboUnpackUShort(buf, maxShots);
178 buf = nboUnpackUShort(buf, shakeWins);
179 buf = nboUnpackUShort(buf, shakeTimeout);
180 buf = nboUnpackUShort(buf, maxPlayerScore);
181 buf = nboUnpackUShort(buf, maxTeamScore);
182 buf = nboUnpackUShort(buf, maxTime);
183 buf = nboUnpackUByte(buf, maxPlayers);
184 buf = nboUnpackUByte(buf, rogueCount);
185 buf = nboUnpackUByte(buf, rogueMax);
186 buf = nboUnpackUByte(buf, redCount);
187 buf = nboUnpackUByte(buf, redMax);
188 buf = nboUnpackUByte(buf, greenCount);
189 buf = nboUnpackUByte(buf, greenMax);
190 buf = nboUnpackUByte(buf, blueCount);
191 buf = nboUnpackUByte(buf, blueMax);
192 buf = nboUnpackUByte(buf, purpleCount);
193 buf = nboUnpackUByte(buf, purpleMax);
194 buf = nboUnpackUByte(buf, observerCount);
195 buf = nboUnpackUByte(buf, observerMax);
196 return buf;
197 }
198
pack(void * buf,const char * version) const199 void* PingPacket::pack(void* buf, const char* version) const
200 {
201 buf = nboPackString(buf, version, 8);
202 buf = serverId.pack(buf);
203 buf = sourceAddr.pack(buf);
204 buf = nboPackUShort(buf, gameType);
205 buf = nboPackUShort(buf, gameOptions);
206 buf = nboPackUShort(buf, maxShots);
207 buf = nboPackUShort(buf, shakeWins);
208 buf = nboPackUShort(buf, shakeTimeout); // 1/10ths of second
209 buf = nboPackUShort(buf, maxPlayerScore);
210 buf = nboPackUShort(buf, maxTeamScore);
211 buf = nboPackUShort(buf, maxTime);
212 buf = nboPackUByte(buf, maxPlayers);
213 buf = nboPackUByte(buf, rogueCount);
214 buf = nboPackUByte(buf, rogueMax);
215 buf = nboPackUByte(buf, redCount);
216 buf = nboPackUByte(buf, redMax);
217 buf = nboPackUByte(buf, greenCount);
218 buf = nboPackUByte(buf, greenMax);
219 buf = nboPackUByte(buf, blueCount);
220 buf = nboPackUByte(buf, blueMax);
221 buf = nboPackUByte(buf, purpleCount);
222 buf = nboPackUByte(buf, purpleMax);
223 buf = nboPackUByte(buf, observerCount);
224 buf = nboPackUByte(buf, observerMax);
225 return buf;
226 }
227
packHex(char * buf) const228 void PingPacket::packHex(char* buf) const
229 {
230 buf = packHex16(buf, gameType);
231 buf = packHex16(buf, gameOptions);
232 buf = packHex16(buf, maxShots);
233 buf = packHex16(buf, shakeWins);
234 buf = packHex16(buf, shakeTimeout);
235 buf = packHex16(buf, maxPlayerScore);
236 buf = packHex16(buf, maxTeamScore);
237 buf = packHex16(buf, maxTime);
238 buf = packHex8(buf, maxPlayers);
239 buf = packHex8(buf, rogueCount);
240 buf = packHex8(buf, rogueMax);
241 buf = packHex8(buf, redCount);
242 buf = packHex8(buf, redMax);
243 buf = packHex8(buf, greenCount);
244 buf = packHex8(buf, greenMax);
245 buf = packHex8(buf, blueCount);
246 buf = packHex8(buf, blueMax);
247 buf = packHex8(buf, purpleCount);
248 buf = packHex8(buf, purpleMax);
249 buf = packHex8(buf, observerCount);
250 buf = packHex8(buf, observerMax);
251 *buf = 0;
252 }
253
unpackHex(char * buf)254 void PingPacket::unpackHex(char* buf)
255 {
256 buf = unpackHex16(buf, gameType);
257 buf = unpackHex16(buf, gameOptions);
258 buf = unpackHex16(buf, maxShots);
259 buf = unpackHex16(buf, shakeWins);
260 buf = unpackHex16(buf, shakeTimeout);
261 buf = unpackHex16(buf, maxPlayerScore);
262 buf = unpackHex16(buf, maxTeamScore);
263 buf = unpackHex16(buf, maxTime);
264 buf = unpackHex8(buf, maxPlayers);
265 buf = unpackHex8(buf, rogueCount);
266 buf = unpackHex8(buf, rogueMax);
267 buf = unpackHex8(buf, redCount);
268 buf = unpackHex8(buf, redMax);
269 buf = unpackHex8(buf, greenCount);
270 buf = unpackHex8(buf, greenMax);
271 buf = unpackHex8(buf, blueCount);
272 buf = unpackHex8(buf, blueMax);
273 buf = unpackHex8(buf, purpleCount);
274 buf = unpackHex8(buf, purpleMax);
275 buf = unpackHex8(buf, observerCount);
276 buf = unpackHex8(buf, observerMax);
277 }
278
hex2bin(char d)279 int PingPacket::hex2bin(char d)
280 {
281 switch (d)
282 {
283 case '0':
284 return 0;
285 case '1':
286 return 1;
287 case '2':
288 return 2;
289 case '3':
290 return 3;
291 case '4':
292 return 4;
293 case '5':
294 return 5;
295 case '6':
296 return 6;
297 case '7':
298 return 7;
299 case '8':
300 return 8;
301 case '9':
302 return 9;
303 case 'A':
304 case 'a':
305 return 10;
306 case 'B':
307 case 'b':
308 return 11;
309 case 'C':
310 case 'c':
311 return 12;
312 case 'D':
313 case 'd':
314 return 13;
315 case 'E':
316 case 'e':
317 return 14;
318 case 'F':
319 case 'f':
320 return 15;
321 }
322 return 0;
323 }
324
bin2hex(int d)325 char PingPacket::bin2hex(int d)
326 {
327 static const char* digit = "0123456789abcdef";
328 return digit[d];
329 }
330
packHex16(char * buf,uint16_t v)331 char* PingPacket::packHex16(char* buf, uint16_t v)
332 {
333 *buf++ = bin2hex((v >> 12) & 0xf);
334 *buf++ = bin2hex((v >> 8) & 0xf);
335 *buf++ = bin2hex((v >> 4) & 0xf);
336 *buf++ = bin2hex( v & 0xf);
337 return buf;
338 }
339
unpackHex16(char * buf,uint16_t & v)340 char* PingPacket::unpackHex16(char* buf, uint16_t& v)
341 {
342 uint16_t d = 0;
343 d = (d << 4) | hex2bin(*buf++);
344 d = (d << 4) | hex2bin(*buf++);
345 d = (d << 4) | hex2bin(*buf++);
346 d = (d << 4) | hex2bin(*buf++);
347 v = d;
348 return buf;
349 }
350
packHex8(char * buf,uint8_t v)351 char* PingPacket::packHex8(char* buf, uint8_t v)
352 {
353 *buf++ = bin2hex((v >> 4) & 0xf);
354 *buf++ = bin2hex( v & 0xf);
355 return buf;
356 }
357
unpackHex8(char * buf,uint8_t & v)358 char* PingPacket::unpackHex8(char* buf, uint8_t& v)
359 {
360 uint16_t d = 0;
361 d = (d << 4) | hex2bin(*buf++);
362 d = (d << 4) | hex2bin(*buf++);
363 v = (uint8_t)d;
364 return buf;
365 }
366
zeroPlayerCounts()367 void PingPacket::zeroPlayerCounts()
368 {
369 rogueCount = 0;
370 redCount = 0;
371 greenCount = 0;
372 blueCount = 0;
373 purpleCount = 0;
374 observerCount = 0;
375 }
376
377 // serialize packet to file -- note lack of error checking
378 // must write to a binary file if we use plain "pack"
writeToFile(std::ostream & out) const379 void PingPacket::writeToFile (std::ostream& out) const
380 {
381 if (!out) return;
382
383 char buffer[PingPacket::PacketSize];
384 void* buf = buffer;
385 buf = nboPackUShort(buf, PingPacket::PacketSize - 4);
386 buf = nboPackUShort(buf, MsgPingCodeReply);
387 buf = pack(buf, getServerVersion());
388 out.write(buffer,sizeof(buffer));
389 }
390
391 // de serialize packet from file
392 // must read from a binary file if we use plain "unpack"
readFromFile(std::istream & in)393 bool PingPacket::readFromFile(std::istream& in)
394 {
395 if (!in) return false;
396
397 char buffer[PingPacket::PacketSize], serverVersion[9];
398 uint16_t len, code;
399
400 // get packet
401 in.read(buffer, sizeof(buffer));
402 if ((size_t)in.gcount() < sizeof(buffer))
403 return false;
404
405 // decode header
406 const void* buf = buffer;
407 buf = nboUnpackUShort(buf, len);
408 buf = nboUnpackUShort(buf, code);
409
410 // make sure we got the rest of the message
411 if (len != in.gcount() - 4)
412 return false;
413
414 // unpack body of reply
415 buf = unpack(buf, serverVersion);
416
417 // compare protocol version against my protocol version.
418 return (strncmp(serverVersion, getServerVersion(), 8) == 0);
419 }
420
421 // Local Variables: ***
422 // mode: C++ ***
423 // tab-width: 4 ***
424 // c-basic-offset: 4 ***
425 // indent-tabs-mode: nil ***
426 // End: ***
427 // ex: shiftwidth=4 tabstop=4
428