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 "ServerLink.h"
15 // #include "MsgStrings.h"
16
17 #if defined(DEBUG)
18 #define NETWORK_STATS
19 #endif
20
21 // system headers
22 #include <string.h>
23 #include <sys/types.h>
24 #include <ctype.h>
25 #include <time.h>
26 #include <vector>
27 #if !defined(_WIN32)
28 #include <unistd.h>
29 #include <errno.h>
30 #endif
31 #include <stdio.h>
32 #include <stdlib.h>
33
34 // common implementation headers
35 #include "ErrorHandler.h"
36 // invoke persistent rebuilding for current version dates
37 #include "version.h"
38 #if defined(NETWORK_STATS)
39 #include "bzfio.h"
40 #endif
41 #include "TimeKeeper.h"
42
43 #define UDEBUG if (UDEBUGMSG) printf
44 #define UDEBUGMSG false
45
46 #if defined(NETWORK_STATS)
47 static TimeKeeper startTime;
48 static uint32_t bytesSent;
49 static uint32_t bytesReceived;
50 static uint32_t packetsSent;
51 static uint32_t packetsReceived;
52 #endif
53
54 #if defined(_WIN32)
55 DWORD ThreadID; // Thread ID
56 HANDLE hConnected; // "Connected" event
57 HANDLE hThread; // Connection thread
58
59 typedef struct
60 {
61 int query;
62 CNCTType* addr;
63 int saddr;
64 } TConnect;
65
66 TConnect conn;
67
ThreadConnect(LPVOID params)68 DWORD WINAPI ThreadConnect(LPVOID params)
69 {
70 TConnect *conn = (TConnect*)params;
71 if (connect(conn->query, conn->addr, conn->saddr) >= 0)
72 {
73 SetEvent(hConnected); // Connect successful
74 }
75 ExitThread(0);
76 return 0;
77 }
78
79 #endif // !defined(_WIN32)
80
81 // FIXME -- packet recording
82 FILE* packetStream = NULL;
83 TimeKeeper packetStartTime;
84 static const unsigned long serverPacket = 1;
85 static const unsigned long endPacket = 0;
86
87 ServerLink* ServerLink::server = NULL;
88
ServerLink(const Address & serverAddress,int port)89 ServerLink::ServerLink(const Address& serverAddress, int port) :
90 state(SocketError), // assume failure
91 fd(-1), // assume failure
92 udpLength(0),
93 udpBufferPtr(),
94 ubuf()
95 {
96 int i;
97
98 struct protoent* p;
99 #if defined(_WIN32)
100 BOOL off = FALSE;
101 #else
102 int off = 0;
103 #endif
104
105 // standard server has no special abilities;
106 server_abilities = Nothing;
107
108 // queue is empty
109
110 urecvfd = -1;
111
112 ulinkup = false;
113
114 // initialize version to a bogus number
115 strcpy(version, "BZFS0000");
116
117 // open connection to server. first connect to given port.
118 // don't wait too long.
119 int query = socket(AF_INET, SOCK_STREAM, 0);
120 if (query < 0) return;
121
122 struct sockaddr_in addr;
123 addr.sin_family = AF_INET;
124 addr.sin_port = htons(port);
125 addr.sin_addr = serverAddress;
126
127 UDEBUG("Remote %s\n", inet_ntoa(addr.sin_addr));
128
129 // for UDP, used later
130 memcpy((unsigned char *)&usendaddr,(unsigned char *)&addr, sizeof(addr));
131
132 bool okay = true;
133 int fdMax = query;
134 struct timeval timeout;
135 fd_set write_set;
136 fd_set read_set;
137 int nfound;
138
139 #if !defined(_WIN32)
140 okay = true;
141 fdMax = query;
142 if (BzfNetwork::setNonBlocking(query) < 0)
143 {
144 close(query);
145 return;
146 }
147 if (connect(query, (CNCTType*)&addr, sizeof(addr)) < 0)
148 {
149 if (getErrno() != EINPROGRESS)
150 {
151 close(query);
152 return;
153 }
154 FD_ZERO(&write_set);
155 FD_SET((unsigned int)query, &write_set);
156 timeout.tv_sec = long(5);
157 timeout.tv_usec = 0;
158 nfound = select(fdMax + 1, NULL, (fd_set*)&write_set, NULL, &timeout);
159 if (nfound <= 0)
160 {
161 close(query);
162 return;
163 }
164 int connectError;
165 socklen_t errorLen = sizeof(int);
166 if (getsockopt(query, SOL_SOCKET, SO_ERROR, &connectError, &errorLen)
167 < 0)
168 {
169 close(query);
170 return;
171 }
172 if (connectError != 0)
173 {
174 close(query);
175 return;
176 }
177 }
178 #else // Connection timeout for Windows
179
180 // Initialize structure
181 conn.query = query;
182 conn.addr = (CNCTType*)&addr;
183 conn.saddr = sizeof(addr);
184
185 // Create event
186 hConnected = CreateEvent(NULL, FALSE, FALSE, "Connected Event");
187
188 hThread = CreateThread(NULL, 0, ThreadConnect, &conn, 0, &ThreadID);
189 okay = (WaitForSingleObject(hConnected, 5000) == WAIT_OBJECT_0);
190 if (!okay)
191 TerminateThread(hThread, 1);
192
193 // Do some cleanup
194 CloseHandle(hConnected);
195 CloseHandle(hThread);
196
197 #endif // !defined(_WIN32)
198 if (!okay)
199 {
200 close(query);
201 return;
202 }
203
204 // send out the connect header
205 // this will let the server know we are BZFS protocol.
206 // after the server gets this it will send back a version for us to check
207 int sendRepply = ::send(query,BZ_CONNECT_HEADER,(int)strlen(BZ_CONNECT_HEADER),0);
208
209 logDebugMessage(2,"CONNECT:send in connect returned %d\n",sendRepply);
210
211 // wait to get data back. we are still blocking so these
212 // calls should be sync.
213
214 FD_ZERO(&read_set);
215 FD_ZERO(&write_set);
216 FD_SET((unsigned int)query, &read_set);
217 FD_SET((unsigned int)query, &write_set);
218
219 timeout.tv_sec = long(10);
220 timeout.tv_usec = 0;
221
222 // pick some limit to time out on ( in seconds )
223 double thisStartTime = TimeKeeper::getCurrent().getSeconds();
224 double connectTimeout = 30.0;
225 if (BZDB.isSet("connectionTimeout"))
226 connectTimeout = BZDB.eval("connectionTimeout") ;
227
228 bool gotNetData = false;
229
230 // loop calling select untill we read some data back.
231 // its only 8 bytes so it better come back in one packet.
232 int loopCount = 0;
233 while (!gotNetData)
234 {
235 loopCount++;
236 nfound = select(fdMax + 1, (fd_set*)&read_set, (fd_set*)&write_set, NULL, &timeout);
237
238 // there has to be at least one socket active, or we are screwed
239 if (nfound <= 0)
240 {
241 logDebugMessage(1,"CONNECT:select in connect failed, nfound = %d\n",nfound);
242 close(query);
243 return;
244 }
245
246 // try and get data back from the server
247 i = recv(query, (char*)version, 8, 0);
248
249 // if we got some, then we are done
250 if (i > 0)
251 {
252 logDebugMessage(2,"CONNECT:got net data in connect, bytes read = %d\n",i);
253 logDebugMessage(2,"CONNECT:Time To Connect = %f\n",(TimeKeeper::getCurrent().getSeconds() - thisStartTime));
254 gotNetData = true;
255 }
256 else
257 {
258 // if we have waited too long, then bail
259 if ((TimeKeeper::getCurrent().getSeconds() - thisStartTime) > connectTimeout)
260 {
261 logDebugMessage(1,"CONNECT:connect time out failed\n");
262 logDebugMessage(2,"CONNECT:connect loop count = %d\n",loopCount);
263 close(query);
264 return;
265 }
266
267 TimeKeeper::sleep(0.25f);
268 }
269 }
270
271 logDebugMessage(2,"CONNECT:connect loop count = %d\n",loopCount);
272
273 // if we got back less than the expected connect response (BZFSXXXX)
274 // then something went bad, and we are done.
275 if (i < 8)
276 {
277 close(query);
278 return;
279 }
280
281 // since we are connected, we can go non blocking
282 // on BSD sockets systems
283 // all other messages after this are handled via the normal
284 // message system
285 #if !defined(_WIN32)
286 if (BzfNetwork::setNonBlocking(query) < 0)
287 {
288 close(query);
289 return;
290 }
291 #endif
292
293 // FIXME is it ok to try UDP always?
294 server_abilities |= CanDoUDP;
295 if (strcmp(version, getServerVersion()) != 0)
296 {
297 state = BadVersion;
298
299 if (strcmp(version, BanRefusalString) == 0)
300 {
301 state = Refused;
302 char message[512];
303 int len = recv(query, (char*)message, 512, 0);
304 if (len > 0)
305 message[len - 1] = 0;
306 else
307 message[0] = 0;
308 rejectionMessage = message;
309 }
310
311 close(query);
312 return;
313 }
314
315 // read local player's id
316 #if !defined(_WIN32)
317 FD_ZERO(&read_set);
318 FD_SET((unsigned int)query, &read_set);
319 timeout.tv_sec = long(5);
320 timeout.tv_usec = 0;
321 nfound = select(fdMax + 1, (fd_set*)&read_set, NULL, NULL, &timeout);
322 if (nfound <= 0)
323 {
324 close(query);
325 return;
326 }
327 #endif // !defined(_WIN32)
328 i = recv(query, (char *) &id, sizeof(id), 0);
329 if (i < (int) sizeof(id))
330 return;
331 if (id == 0xff)
332 {
333 state = Rejected;
334 close(query);
335 return;
336 }
337
338 #if !defined(_WIN32)
339 if (BzfNetwork::setBlocking(query) < 0)
340 {
341 close(query);
342 return;
343 }
344 #endif // !defined(_WIN32)
345
346 fd = query;
347
348 // turn on TCP no delay
349 p = getprotobyname("tcp");
350 if (p)
351 setsockopt(fd, p->p_proto, TCP_NODELAY, (SSOType)&off, sizeof(off)); // changed
352
353 state = Okay;
354 #if defined(NETWORK_STATS)
355 bytesSent = 0;
356 bytesReceived = 0;
357 packetsSent = 0;
358 packetsReceived = 0;
359 #endif
360
361 // FIXME -- packet recording
362 if (getenv("BZFLAGSAVE"))
363 {
364 packetStream = fopen(getenv("BZFLAGSAVE"), "w");
365 packetStartTime = TimeKeeper::getCurrent();
366 }
367 return;
368
369 }
370
~ServerLink()371 ServerLink::~ServerLink()
372 {
373 if (state != Okay) return;
374 shutdown(fd, 2);
375 close(fd);
376
377 if (urecvfd >= 0)
378 close(urecvfd);
379
380 urecvfd = -1;
381 ulinkup = false;
382
383 // FIXME -- packet recording
384 if (packetStream)
385 {
386 long dt = (long)((TimeKeeper::getCurrent() - packetStartTime) * 10000.0f);
387 size_t items_written = fwrite(&endPacket, sizeof(endPacket), 1, packetStream);
388 if (items_written == 1)
389 items_written = fwrite(&dt, sizeof(dt), 1, packetStream);
390 if (items_written != 1)
391 printError("Error writing on packetStream");
392 fclose(packetStream);
393 }
394
395 #if defined(NETWORK_STATS)
396 const float dt = float(TimeKeeper::getCurrent() - startTime);
397 logDebugMessage(1,"Server network statistics:\n");
398 logDebugMessage(1," elapsed time : %f\n", dt);
399 logDebugMessage(1," bytes sent : %d (%f/sec)\n", bytesSent, (float)bytesSent / dt);
400 logDebugMessage(1," packets sent : %d (%f/sec)\n", packetsSent, (float)packetsSent / dt);
401 if (packetsSent != 0)
402 logDebugMessage(1," bytes/packet : %f\n", (float)bytesSent / (float)packetsSent);
403 logDebugMessage(1," bytes recieved : %d (%f/sec)\n", bytesReceived, (float)bytesReceived / dt);
404 logDebugMessage(1," packets received: %d (%f/sec)\n", packetsReceived, (float)packetsReceived / dt);
405 if (packetsReceived != 0)
406 logDebugMessage(1," bytes/packet : %f\n", (float)bytesReceived / (float)packetsReceived);
407 #endif
408 }
409
getServer()410 ServerLink* ServerLink::getServer() // const
411 {
412 return server;
413 }
414
setServer(ServerLink * _server)415 void ServerLink::setServer(ServerLink* _server)
416 {
417 server = _server;
418 }
419
send(uint16_t code,uint16_t len,const void * msg)420 void ServerLink::send(uint16_t code, uint16_t len,
421 const void* msg)
422 {
423 bool needForSpeed=false;
424 if (state != Okay) return;
425 // if (code != MsgPlayerUpdateSmall && code != MsgPlayerUpdate)
426 // logDebugMessage(1,"send %s len %d\n",MsgStrings::strMsgCode(code),len);
427 char msgbuf[MaxPacketLen];
428 void* buf = msgbuf;
429 buf = nboPackUShort(buf, len);
430 buf = nboPackUShort(buf, code);
431 if (msg && len != 0) buf = nboPackString(buf, msg, len);
432
433 if ((urecvfd>=0) && ulinkup )
434 {
435 switch (code)
436 {
437 case MsgShotBegin:
438 case MsgShotEnd:
439 case MsgPlayerUpdate:
440 case MsgPlayerUpdateSmall:
441 case MsgGMUpdate:
442 case MsgUDPLinkRequest:
443 case MsgUDPLinkEstablished:
444 needForSpeed=true;
445 break;
446 }
447 }
448 // MsgUDPLinkRequest always goes udp
449 if (code == MsgUDPLinkRequest)
450 needForSpeed=true;
451
452 if (needForSpeed)
453 {
454 #ifdef TESTLINK
455 if ((random()%TESTQUALTIY) != 0)
456 #endif
457 sendto(urecvfd, (const char *)msgbuf, (char*)buf - msgbuf, 0, &usendaddr, sizeof(usendaddr));
458 // we don't care about errors yet
459 return;
460 }
461
462 int r = ::send(fd, (const char*)msgbuf, len + 4, 0);
463 (void)r; // silence g++
464 #if defined(_WIN32)
465 if (r == SOCKET_ERROR)
466 {
467 const int e = WSAGetLastError();
468 if (e == WSAENETRESET || e == WSAECONNABORTED ||
469 e == WSAECONNRESET || e == WSAETIMEDOUT)
470 state = Hungup;
471 r = 0;
472 }
473 #endif
474
475 #if defined(NETWORK_STATS)
476 bytesSent += r;
477 packetsSent++;
478 #endif
479 }
480
481 #ifdef WIN32
482 /* This is a really really fugly hack to get around winsock sillyness
483 * The newer versions of winsock have a socken_t typedef, and there
484 * doesn't seem to be any way to tell the versions apart. However,
485 * VC++ helps us out here by treating typedef as #define
486 * If we've got a socklen_t typedefed, define HAVE_SOCKLEN_T to
487 * avoid #define'ing it in common.h */
488
489 #ifndef socklen_t
490 #define socklen_t int
491 #endif
492 #endif //WIN32
493
read(uint16_t & code,uint16_t & len,void * msg,int blockTime)494 int ServerLink::read(uint16_t& code, uint16_t& len,
495 void* msg, int blockTime)
496 {
497
498 code = MsgNull;
499 len = 0;
500
501 if (state != Okay) return -1;
502
503 if ((urecvfd >= 0) /* && ulinkup */)
504 {
505
506 if (!udpLength)
507 {
508 AddrLen recvlen = sizeof(urecvaddr);
509 int n = recvfrom(urecvfd, ubuf, MaxPacketLen, 0,
510 &urecvaddr, (socklen_t*) &recvlen);
511 if (n > 0)
512 {
513 udpLength = n;
514 udpBufferPtr = ubuf;
515 }
516 }
517 if (udpLength)
518 {
519 // unpack header and get message
520 udpLength -= 4;
521 if (udpLength < 0)
522 {
523 udpLength = 0;
524 return -1;
525 }
526 udpBufferPtr = (const char *)nboUnpackUShort(udpBufferPtr, len);
527 udpBufferPtr = (const char *)nboUnpackUShort(udpBufferPtr, code);
528 // if (code != MsgPlayerUpdateSmall && code != MsgPlayerUpdate && code != MsgGameTime)
529 // logDebugMessage(1,"rcvd %s len %d\n",MsgStrings::strMsgCode(code),len);
530 UDEBUG("<** UDP Packet Code %x Len %x\n",code, len);
531 if (len > udpLength)
532 {
533 udpLength = 0;
534 return -1;
535 }
536 memcpy((char *)msg, udpBufferPtr, len);
537 udpBufferPtr += len;
538 udpLength -= len;
539 return 1;
540 }
541 if (UDEBUGMSG) printError("Fallback to normal TCP receive");
542 len = 0;
543 code = MsgNull;
544
545 blockTime = 0;
546 }
547
548 // block for specified period. default is no blocking (polling)
549 struct timeval timeout;
550 timeout.tv_sec = blockTime / 1000;
551 timeout.tv_usec = blockTime - 1000 * timeout.tv_sec;
552
553 // only check server
554 fd_set read_set;
555 FD_ZERO(&read_set);
556 FD_SET((unsigned int)fd, &read_set);
557 int nfound = select(fd+1, (fd_set*)&read_set, NULL, NULL,
558 (struct timeval*)(blockTime >= 0 ? &timeout : NULL));
559 if (nfound == 0) return 0;
560 if (nfound < 0) return -1;
561
562 // printError("<** TCP Packet Code Received %d", time(0));
563 // FIXME -- don't really want to take the chance of waiting forever
564 // on the remaining select() calls, but if the server and network
565 // haven't been hosed then the data will get here soon. And if the
566 // server or network is down then we don't really care anyway.
567
568 // get packet header -- keep trying until we get 4 bytes or an error
569 char headerBuffer[4];
570
571
572 int rlen = 0;
573 rlen = recv(fd, (char*)headerBuffer, 4, 0);
574
575 int tlen = rlen;
576 while (rlen >= 1 && tlen < 4)
577 {
578 printError("ServerLink::read() loop");
579 FD_ZERO(&read_set);
580 FD_SET((unsigned int)fd, &read_set);
581 nfound = select(fd+1, (fd_set*)&read_set, NULL, NULL, NULL);
582 if (nfound == 0) continue;
583 if (nfound < 0) return -1;
584 rlen = recv(fd, (char*)headerBuffer + tlen, 4 - tlen, 0);
585 if (rlen >= 0) tlen += rlen;
586 }
587 if (tlen < 4) return -1;
588 #if defined(NETWORK_STATS)
589 bytesReceived += 4;
590 packetsReceived++;
591 #endif
592
593 // unpack header and get message
594 const void* buf = headerBuffer;
595 buf = nboUnpackUShort(buf, len);
596 buf = nboUnpackUShort(buf, code);
597
598 // logDebugMessage(1,"rcvd %s len %d\n",MsgStrings::strMsgCode(code),len);
599 if (len > MaxPacketLen - 4)
600 return -1;
601 if (len > 0)
602 rlen = recv(fd, (char*)msg, int(len), 0);
603 else
604 rlen = 0;
605 #if defined(NETWORK_STATS)
606 if (rlen >= 0) bytesReceived += rlen;
607 #endif
608 if (rlen != int(len))
609 {
610 // keep reading until we get the whole message
611 tlen = rlen;
612 while (rlen >= 1 && tlen < int(len))
613 {
614 FD_ZERO(&read_set);
615 FD_SET((unsigned int)fd, &read_set);
616 nfound = select(fd+1, (fd_set*)&read_set, 0, 0, NULL);
617 if (nfound == 0) continue;
618 if (nfound < 0) return -1;
619 rlen = recv(fd, (char*)msg + tlen, int(len) - tlen, 0);
620 if (rlen >= 0) tlen += rlen;
621 #if defined(NETWORK_STATS)
622 if (rlen >= 0) bytesReceived += rlen;
623 #endif
624 }
625 if (tlen < int(len)) return -1;
626 }
627
628 // FIXME -- packet recording
629 if (packetStream)
630 {
631 long dt = (long)((TimeKeeper::getCurrent() - packetStartTime) * 10000.0f);
632 size_t items_written = fwrite(&serverPacket, sizeof(serverPacket), 1, packetStream);
633 if (items_written == 1)
634 items_written = fwrite(&dt, sizeof(dt), 1, packetStream);
635 if (items_written == 1)
636 items_written = fwrite(headerBuffer, 4, 1, packetStream);
637 if (items_written == 1)
638 items_written = fwrite(msg, len, 1, packetStream);
639 if (items_written != 1)
640 printError("Error writing on packetStream");
641 }
642 return 1;
643 }
644
sendEnter(PlayerType type,TeamColor team,const char * name,const char * motto,const char * token)645 void ServerLink::sendEnter(PlayerType type,
646 TeamColor team,
647 const char* name,
648 const char* motto,
649 const char* token)
650 {
651 if (state != Okay) return;
652 char msg[PlayerIdPLen + 4 + CallSignLen + MottoLen + TokenLen + VersionLen];
653 ::memset(msg, 0, sizeof(msg));
654 void* buf = msg;
655 buf = nboPackUShort(buf, uint16_t(type));
656 buf = nboPackUShort(buf, uint16_t(team));
657 ::memcpy(buf, name, ::strlen(name));
658 buf = (void*)((char*)buf + CallSignLen);
659 ::memcpy(buf, motto, ::strlen(motto));
660 buf = (void*)((char*)buf + MottoLen);
661 ::memcpy(buf, token, ::strlen(token));
662 buf = (void*)((char*)buf + TokenLen);
663 ::memcpy(buf, getAppVersion(), ::strlen(getAppVersion()) + 1);
664 buf = (void*)((char*)buf + VersionLen);
665 send(MsgEnter, sizeof(msg), msg);
666 }
667
readEnter(std::string & reason,uint16_t & code,uint16_t & rejcode)668 bool ServerLink::readEnter(std::string& reason,
669 uint16_t& code, uint16_t& rejcode)
670 {
671 // wait for response
672 uint16_t len;
673 char msg[MaxPacketLen];
674
675 while (true)
676 {
677 if (this->read(code, len, msg, -1) < 0)
678 {
679 reason = "Communication error joining game [No immediate respose].";
680 return false;
681 }
682
683 if (code == MsgAccept)
684 return true;
685 else if (code == MsgSuperKill)
686 {
687 reason = "Server forced disconnection.";
688 return false;
689 }
690 else if (code == MsgReject)
691 {
692 const void *buf;
693 char buffer[MessageLen];
694 buf = nboUnpackUShort (msg, rejcode); // filler for now
695 buf = nboUnpackString (buf, buffer, MessageLen);
696 buffer[MessageLen - 1] = '\0';
697 reason = buffer;
698 return false;
699 }
700 // ignore other codes so that bzadmin doesn't choke
701 // on the MsgMessage's that the server can send before
702 // the MsgAccept (authorization holdoff, etc...)
703 }
704
705 return true;
706 }
707
708 // Local Variables: ***
709 // mode: C++ ***
710 // tab-width: 4 ***
711 // c-basic-offset: 4 ***
712 // indent-tabs-mode: nil ***
713 // End: ***
714 // ex: shiftwidth=4 tabstop=4
715