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