1 /*
2 * OpenClonk, http://www.openclonk.org
3 *
4 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5 * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16 #include "C4Include.h"
17 #include "network/C4Network2IO.h"
18
19 #include "control/C4GameControl.h"
20 #include "game/C4Application.h"
21 #include "network/C4Network2Discover.h"
22 #include "network/C4Network2Reference.h"
23 #include "network/C4Network2UPnP.h"
24
25 #ifndef HAVE_WINSOCK
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #endif
30
31 // internal structures
32 struct C4Network2IO::NetEvPacketData
33 {
34 C4NetIOPacket Packet;
35 C4Network2IOConnection *Conn;
36 };
37
38 // compile options
39 #define C4NET2IO_DUMP_LEVEL 1
40
41 // *** C4Network2IO
42
C4Network2IO()43 C4Network2IO::C4Network2IO()
44 : tLastExecute(0), tLastPing(0), tLastStatistic(0)
45 {
46 }
47
~C4Network2IO()48 C4Network2IO::~C4Network2IO()
49 {
50 Clear();
51 }
52
Init(int16_t iPortTCP,int16_t iPortUDP,int16_t iPortDiscover,int16_t iPortRefServer,bool fBroadcast,bool enable_upnp)53 bool C4Network2IO::Init(int16_t iPortTCP, int16_t iPortUDP, int16_t iPortDiscover, int16_t iPortRefServer, bool fBroadcast, bool enable_upnp) // by main thread
54 {
55 // Already initialized? Clear first
56 if (pNetIO_TCP || pNetIO_UDP) Clear();
57
58 // init members
59 tLastPing = tLastStatistic = C4TimeMilliseconds::Now();
60 iTCPIRate = iTCPORate = iTCPBCRate = 0;
61 iUDPIRate = iUDPORate = iUDPBCRate = 0;
62
63 // init event callback
64 C4InteractiveThread &Thread = Application.InteractiveThread;
65 Thread.SetCallback(Ev_Net_Conn, this);
66 Thread.SetCallback(Ev_Net_Disconn, this);
67 Thread.SetCallback(Ev_Net_Packet, this);
68
69 // initialize UPnP manager
70 if (enable_upnp && (iPortTCP > 0 || iPortUDP > 0))
71 {
72 assert(!UPnPMgr);
73 UPnPMgr = new C4Network2UPnP;
74 }
75
76 // initialize net i/o classes: TCP first
77 if (iPortTCP > 0)
78 {
79 // create
80 pNetIO_TCP = new C4NetIOTCP();
81 // init
82 if (!pNetIO_TCP->Init(iPortTCP))
83 {
84 LogF("Network: could not init TCP i/o (%s)", pNetIO_TCP->GetError() ? pNetIO_TCP->GetError() : "");
85 delete pNetIO_TCP; pNetIO_TCP = nullptr;
86 }
87 else
88 LogSilentF("Network: TCP initialized on port %d", iPortTCP);
89
90 // add to thread, set callback
91 if (pNetIO_TCP)
92 {
93 Thread.AddProc(pNetIO_TCP);
94 pNetIO_TCP->SetCallback(this);
95 if (UPnPMgr) UPnPMgr->AddMapping(P_TCP, iPortTCP, iPortTCP);
96 }
97
98 }
99 // then UDP
100 if (iPortUDP > 0)
101 {
102 // create
103 pNetIO_UDP = new C4NetIOUDP();
104 // init
105 if (!pNetIO_UDP->Init(iPortUDP))
106 {
107 LogF("Network: could not init UDP i/o (%s)", pNetIO_UDP->GetError() ? pNetIO_UDP->GetError() : "");
108 delete pNetIO_UDP; pNetIO_UDP = nullptr;
109 }
110 else
111 LogSilentF("Network: UDP initialized on port %d", iPortUDP);
112
113 // add to thread, set callback
114 if (pNetIO_UDP)
115 {
116 Thread.AddProc(pNetIO_UDP);
117 pNetIO_UDP->SetCallback(this);
118 if (UPnPMgr) UPnPMgr->AddMapping(P_UDP, iPortUDP, iPortUDP);
119 }
120 }
121
122 // no protocols?
123 if (!pNetIO_TCP && !pNetIO_UDP)
124 {
125 LogFatal("Network: fatal - no protocols available!");
126 Thread.ClearCallback(Ev_Net_Conn, this);
127 Thread.ClearCallback(Ev_Net_Disconn, this);
128 Thread.ClearCallback(Ev_Net_Packet, this);
129 return false;
130 }
131
132 // discovery last
133 if (iPortDiscover > 0)
134 {
135 // create
136 pNetIODiscover = new C4Network2IODiscover(iPortRefServer);
137 pNetIODiscover->SetDiscoverable(false);
138 // init
139 if (!pNetIODiscover->Init(iPortDiscover))
140 {
141 LogF("Network: could not init discovery (%s)", pNetIODiscover->GetError() ? pNetIODiscover->GetError() : "");
142 delete pNetIODiscover; pNetIODiscover = nullptr;
143 }
144 else
145 LogSilentF("Network: discovery initialized on port %d", iPortDiscover);
146 // add to thread
147 if (pNetIODiscover)
148 Thread.AddProc(pNetIODiscover);
149 }
150
151 // plus reference server
152 if (iPortRefServer > 0)
153 {
154 // create
155 pRefServer = new C4Network2RefServer();
156 // init
157 if (!pRefServer->Init(iPortRefServer))
158 {
159 LogF("Network: could not init reference server (%s)", pNetIO_UDP->GetError() ? pNetIO_UDP->GetError() : "");
160 delete pRefServer; pRefServer = nullptr;
161 }
162 else
163 LogSilentF("Network: reference server initialized on port %d", iPortRefServer);
164 // add to thread
165 if (pRefServer)
166 Thread.AddProc(pRefServer);
167 }
168
169 // own timer
170 tLastExecute = C4TimeMilliseconds::Now();
171 Thread.AddProc(this);
172
173 // ok
174 return true;
175 }
176
Clear()177 void C4Network2IO::Clear() // by main thread
178 {
179 // process remaining events
180 C4InteractiveThread &Thread = Application.InteractiveThread;
181 Thread.ProcessEvents();
182 // clear event callbacks
183 Thread.ClearCallback(Ev_Net_Conn, this);
184 Thread.ClearCallback(Ev_Net_Disconn, this);
185 Thread.ClearCallback(Ev_Net_Packet, this);
186 // close all connections
187 CStdLock ConnListLock(&ConnListCSec);
188 for (C4Network2IOConnection *pConn = pConnList, *pNext; pConn; pConn = pNext)
189 {
190 pNext = pConn->pNext;
191 // close
192 pConn->Close();
193 RemoveConnection(pConn);
194 }
195 // reset list
196 pConnList = nullptr;
197 ConnListLock.Clear();
198 // close net i/o classes
199 Thread.RemoveProc(this);
200 if (pNetIODiscover) { Thread.RemoveProc(pNetIODiscover); delete pNetIODiscover; pNetIODiscover = nullptr; }
201 if (pNetIO_TCP) { Thread.RemoveProc(pNetIO_TCP); delete pNetIO_TCP; pNetIO_TCP = nullptr; }
202 if (pNetIO_UDP) { Thread.RemoveProc(pNetIO_UDP); delete pNetIO_UDP; pNetIO_UDP = nullptr; }
203 if (pRefServer) { Thread.RemoveProc(pRefServer); delete pRefServer; pRefServer = nullptr; }
204 if (UPnPMgr) { delete UPnPMgr; UPnPMgr = nullptr; }
205 // remove auto-accepts
206 ClearAutoAccept();
207 // reset flags
208 fAllowConnect = fExclusiveConn = false;
209 // reset connection ID
210 iNextConnID = 0;
211 }
212
SetLocalCCore(const C4ClientCore & nCCore)213 void C4Network2IO::SetLocalCCore(const C4ClientCore &nCCore)
214 {
215 CStdLock LCCoreLock(&LCCoreCSec);
216 LCCore = nCCore;
217 }
218
MsgIO()219 C4NetIO *C4Network2IO::MsgIO() // by both
220 {
221 if (pNetIO_UDP) return pNetIO_UDP;
222 if (pNetIO_TCP) return pNetIO_TCP;
223 return nullptr;
224 }
225
DataIO()226 C4NetIO *C4Network2IO::DataIO() // by both
227 {
228 if (pNetIO_TCP) return pNetIO_TCP;
229 if (pNetIO_UDP) return pNetIO_UDP;
230 return nullptr;
231 }
232
Connect(const C4NetIO::addr_t & addr,C4Network2IOProtocol eProt,const C4ClientCore & nCCore,const char * szPassword)233 bool C4Network2IO::Connect(const C4NetIO::addr_t &addr, C4Network2IOProtocol eProt, const C4ClientCore &nCCore, const char *szPassword) // by main thread
234 {
235 // get network class
236 C4NetIO *pNetIO = getNetIO(eProt);
237 if (!pNetIO) return false;
238 // already connected/connecting?
239 if (GetConnectionByConnAddr(addr, pNetIO)) return true;
240 // assign new connection ID, peer address isn't known yet
241 uint32_t iConnID = iNextConnID++;
242 C4NetIO::addr_t paddr;
243 // create connection object and add to list
244 C4Network2IOConnection *pConn = new C4Network2IOConnection();
245 pConn->Set(pNetIO, eProt, paddr, addr, CS_Connect, szPassword, iConnID);
246 pConn->SetCCore(nCCore);
247 AddConnection(pConn);
248 // connect
249 if (!pConn->Connect())
250 {
251 // show error
252 LogSilentF("Network: could not connect to %s using %s: %s", addr.ToString().getData(),
253 getNetIOName(pNetIO), pNetIO->GetError() ? pNetIO->GetError() : "");
254 pNetIO->ResetError();
255 // remove class
256 RemoveConnection(pConn);
257 return false;
258 }
259 // ok, wait for connection
260 return true;
261 }
262
SetAcceptMode(bool fnAllowConnect)263 void C4Network2IO::SetAcceptMode(bool fnAllowConnect) // by main thread
264 {
265 fAllowConnect = fnAllowConnect;
266 // Allow connect? Allow discovery of this host
267 if (fAllowConnect)
268 {
269 if (pNetIODiscover)
270 {
271 pNetIODiscover->SetDiscoverable(true);
272 pNetIODiscover->Announce();
273 }
274 }
275 }
276
SetExclusiveConnMode(bool fnExclusiveConn)277 void C4Network2IO::SetExclusiveConnMode(bool fnExclusiveConn) // by main thread
278 {
279 if (fExclusiveConn == fnExclusiveConn)
280 return;
281 // Set flag
282 fExclusiveConn = fnExclusiveConn;
283 // Allowed? Send all pending welcome packets
284 if (!fExclusiveConn)
285 SendConnPackets();
286 }
287
getConnectionCount()288 int C4Network2IO::getConnectionCount() // by main thread
289 {
290 int iCount = 0;
291 CStdLock ConnListLock(&ConnListCSec);
292 for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
293 if (!pConn->isClosed())
294 iCount++;
295 return iCount;
296 }
297
ClearAutoAccept()298 void C4Network2IO::ClearAutoAccept() // by main thread
299 {
300 CStdLock AALock(&AutoAcceptCSec);
301 // delete
302 while (pAutoAcceptList)
303 {
304 // remove
305 AutoAccept *pAcc = pAutoAcceptList;
306 pAutoAcceptList = pAcc->Next;
307 // delete
308 delete pAcc;
309 }
310 }
311
AddAutoAccept(const C4ClientCore & CCore)312 void C4Network2IO::AddAutoAccept(const C4ClientCore &CCore) // by main thread
313 {
314 CStdLock AALock(&AutoAcceptCSec);
315 // create
316 AutoAccept *pAcc = new AutoAccept();
317 pAcc->CCore = CCore;
318 // add
319 pAcc->Next = pAutoAcceptList;
320 pAutoAcceptList = pAcc;
321 }
322
RemoveAutoAccept(const C4ClientCore & CCore)323 void C4Network2IO::RemoveAutoAccept(const C4ClientCore &CCore) // by main thread
324 {
325 CStdLock AALock(&AutoAcceptCSec);
326 // find & remove
327 AutoAccept *pAcc = pAutoAcceptList, *pLast = nullptr;
328 while (pAcc)
329 if (pAcc->CCore.getDiffLevel(CCore) <= C4ClientCoreDL_IDMatch)
330 {
331 // unlink
332 AutoAccept *pDelete = pAcc;
333 pAcc = pAcc->Next;
334 (pLast ? pLast->Next : pAutoAcceptList) = pAcc;
335 // delete
336 delete pDelete;
337 }
338 else
339 {
340 // next peer
341 pLast = pAcc;
342 pAcc = pAcc->Next;
343 }
344 }
345
GetMsgConnection(int iClientID)346 C4Network2IOConnection *C4Network2IO::GetMsgConnection(int iClientID) // by main thread
347 {
348 CStdLock ConnListLock(&ConnListCSec);
349 C4Network2IOConnection *pRes = nullptr;
350 for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
351 if (pConn->isAccepted())
352 if (pConn->getClientID() == iClientID)
353 if (pConn->getProtocol() == P_UDP || !pRes)
354 pRes = pConn;
355 // add reference
356 if (pRes) pRes->AddRef();
357 return pRes;
358 }
359
GetDataConnection(int iClientID)360 C4Network2IOConnection *C4Network2IO::GetDataConnection(int iClientID) // by main thread
361 {
362 CStdLock ConnListLock(&ConnListCSec);
363 C4Network2IOConnection *pRes = nullptr;
364 for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
365 if (pConn->isAccepted())
366 if (pConn->getClientID() == iClientID)
367 if (pConn->getProtocol() == P_TCP || !pRes)
368 pRes = pConn;
369 // add reference
370 if (pRes) pRes->AddRef();
371 return pRes;
372 }
373
BeginBroadcast(bool fSelectAll)374 void C4Network2IO::BeginBroadcast(bool fSelectAll)
375 {
376 // lock
377 BroadcastCSec.Enter();
378 // reset all broadcast flags
379 CStdLock ConnListLock(&ConnListCSec);
380 for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
381 if (pConn->isOpen())
382 pConn->SetBroadcastTarget(fSelectAll);
383 }
384
EndBroadcast()385 void C4Network2IO::EndBroadcast()
386 {
387 // unlock
388 BroadcastCSec.Leave();
389 }
390
Broadcast(const C4NetIOPacket & rPkt)391 bool C4Network2IO::Broadcast(const C4NetIOPacket &rPkt)
392 {
393 bool fSuccess = true;
394 // There is no broadcasting atm, emulate it
395 CStdLock ConnListLock(&ConnListCSec);
396 for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
397 if (pConn->isOpen() && pConn->isBroadcastTarget())
398 fSuccess &= pConn->Send(rPkt);
399 if(!fSuccess)
400 Log("Network: Warning! Broadcast failed.");
401 return fSuccess;
402 }
403
SendMsgToClient(C4NetIOPacket & rPkt,int iClient)404 bool C4Network2IO::SendMsgToClient(C4NetIOPacket &rPkt, int iClient) // by both
405 {
406 // find msg connection
407 C4Network2IOConnection *pConn = GetMsgConnection(iClient);
408 if (!pConn) return false;
409 // send
410 bool fSuccess = pConn->Send(rPkt);
411 pConn->DelRef();
412 return fSuccess;
413 }
414
BroadcastMsg(const C4NetIOPacket & rPkt)415 bool C4Network2IO::BroadcastMsg(const C4NetIOPacket &rPkt) // by both
416 {
417 // TODO: ugly algorithm. do better
418
419 // begin broadcast
420 BeginBroadcast(false);
421 // select one connection per reachable client
422 CStdLock ConnListLock(&ConnListCSec);
423 for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
424 if (pConn->isAccepted())
425 {
426 if (pConn->getProtocol() == P_UDP)
427 pConn->SetBroadcastTarget(true);
428 else if (pConn->getProtocol() == P_TCP)
429 {
430 C4Network2IOConnection *pConn2 = GetMsgConnection(pConn->getClientID());
431 if (pConn == pConn2)
432 pConn->SetBroadcastTarget(true);
433 pConn2->DelRef();
434 }
435 }
436 // send
437 bool fSuccess = Broadcast(rPkt);
438 // end broadcast
439 EndBroadcast();
440 // return
441 return fSuccess;
442 }
443
InitPuncher(C4NetIO::addr_t nPuncherAddr)444 bool C4Network2IO::InitPuncher(C4NetIO::addr_t nPuncherAddr)
445 {
446 // UDP must be initialized
447 if (!pNetIO_UDP)
448 return false;
449 // save address
450 switch (nPuncherAddr.GetFamily())
451 {
452 case C4NetIO::HostAddress::IPv4:
453 PuncherAddrIPv4 = nPuncherAddr;
454 break;
455 case C4NetIO::HostAddress::IPv6:
456 PuncherAddrIPv6 = nPuncherAddr;
457 break;
458 case C4NetIO::HostAddress::UnknownFamily:
459 assert(!"Unexpected address family");
460 }
461 // let's punch
462 return pNetIO_UDP->Connect(nPuncherAddr);
463 }
464
Punch(const C4NetIO::addr_t & punchee_addr)465 void C4Network2IO::Punch(const C4NetIO::addr_t &punchee_addr)
466 {
467 if (!pNetIO_UDP)
468 return;
469 C4PacketPing PktPeng;
470 dynamic_cast<C4NetIOUDP*>(pNetIO_UDP)->SendDirect(MkC4NetIOPacket(PID_Pong, PktPeng, punchee_addr));
471 }
472
SendPuncherPacket(const C4NetpuncherPacket & p,C4NetIO::HostAddress::AddressFamily family)473 void C4Network2IO::SendPuncherPacket(const C4NetpuncherPacket& p, C4NetIO::HostAddress::AddressFamily family)
474 {
475 if (!pNetIO_UDP) return;
476 if (family == C4NetIO::HostAddress::IPv4 && !PuncherAddrIPv4.IsNull())
477 pNetIO_UDP->Send(p.PackTo(PuncherAddrIPv4));
478 else if (family == C4NetIO::HostAddress::IPv6 && !PuncherAddrIPv6.IsNull())
479 pNetIO_UDP->Send(p.PackTo(PuncherAddrIPv6));
480 }
481
IsPuncherAddr(const C4NetIO::addr_t & addr) const482 bool C4Network2IO::IsPuncherAddr(const C4NetIO::addr_t& addr) const
483 {
484 return (!PuncherAddrIPv4.IsNull() && PuncherAddrIPv4 == addr)
485 || (!PuncherAddrIPv6.IsNull() && PuncherAddrIPv6 == addr);
486 }
487
488 // C4NetIO interface
OnConn(const C4NetIO::addr_t & PeerAddr,const C4NetIO::addr_t & ConnectAddr,const C4NetIO::addr_t * pOwnAddr,C4NetIO * pNetIO)489 bool C4Network2IO::OnConn(const C4NetIO::addr_t &PeerAddr, const C4NetIO::addr_t &ConnectAddr, const C4NetIO::addr_t *pOwnAddr, C4NetIO *pNetIO)
490 {
491 // puncher answer?
492 if (pNetIO == pNetIO_UDP && IsPuncherAddr(ConnectAddr))
493 {
494 // got an address?
495 if (pOwnAddr)
496 ::Network.OnPuncherConnect(*pOwnAddr);
497 return true;
498 }
499
500 #if(C4NET2IO_DUMP_LEVEL > 1)
501 Application.InteractiveThread.ThreadLogS("OnConn: %s %s",
502 C4TimeMilliseconds::Now().AsString().getData(),
503 getNetIOName(pNetIO));
504 #endif
505 // search connection
506 C4Network2IOConnection *pConn = nullptr;
507 if (!ConnectAddr.IsNull())
508 pConn = GetConnectionByConnAddr(ConnectAddr, pNetIO);
509 // not found?
510 if (!pConn)
511 {
512 // allow connect?
513 if (!fAllowConnect) return false;
514 // create new connection object
515 uint32_t iConnID = iNextConnID++;
516 pConn = new C4Network2IOConnection();
517 pConn->Set(pNetIO, getNetIOProt(pNetIO), PeerAddr, ConnectAddr, CS_Connected, nullptr, iConnID);
518 // add to list
519 AddConnection(pConn);
520 }
521 else
522 {
523 // already closed this connection (attempt)?
524 if (pConn->isClosed())
525 return false;
526 if (!pConn->isOpen())
527 {
528 // change status
529 pConn->SetStatus(CS_Connected);
530 pConn->SetPeerAddr(PeerAddr);
531 }
532 }
533 // send welcome packet, if appropriate
534 SendConnPackets();
535 #if(C4NET2IO_DUMP_LEVEL > 0)
536 // log
537 Application.InteractiveThread.ThreadLogS("Network: got %s connection from %s", getNetIOName(pNetIO), PeerAddr.ToString().getData());
538 #endif
539 // do event (disabled - unused)
540 // pConn->AddRef(); PushNetEv(NE_Conn, pConn);
541 // ok
542 return true;
543 }
544
OnDisconn(const C4NetIO::addr_t & addr,C4NetIO * pNetIO,const char * szReason)545 void C4Network2IO::OnDisconn(const C4NetIO::addr_t &addr, C4NetIO *pNetIO, const char *szReason)
546 {
547 // punch?
548 if (pNetIO == pNetIO_UDP && IsPuncherAddr(addr))
549 {
550 if (PuncherAddrIPv4 == addr)
551 PuncherAddrIPv4.Clear();
552 else
553 PuncherAddrIPv6.Clear();
554 return;
555 }
556 #if(C4NET2IO_DUMP_LEVEL > 1)
557 Application.InteractiveThread.ThreadLogS("OnDisconn: %s %s",
558 C4TimeMilliseconds::Now().AsString().getData(),
559 getNetIOName(pNetIO));
560 #endif
561 // find connection
562 C4Network2IOConnection *pConn = GetConnection(addr, pNetIO);
563 if (!pConn) pConn = GetConnectionByConnAddr(addr, pNetIO);
564 if (!pConn) return;
565 #if(C4NET2IO_DUMP_LEVEL > 0)
566 // log
567 Application.InteractiveThread.ThreadLogS("Network: %s connection to %s %s (%s)",
568 getNetIOName(pNetIO), addr.ToString().getData(), pConn->isConnecting() ? "failed" : "closed" , szReason);
569 #endif
570 // already closed? ignore
571 if (!pConn->isClosed())
572 // not accepted yet? count as connection failure
573 pConn->SetStatus(pConn->isHalfAccepted() ? CS_Closed : CS_ConnectFail);
574 // keep connection for main thread message
575 pConn->AddRef();
576 // check for pending welcome packets
577 SendConnPackets();
578 // signal to main thread
579 Application.InteractiveThread.PushEvent(Ev_Net_Disconn, pConn);
580 // don't remove connection from list - wait for postmortem or timeout
581 }
582
OnPacket(const class C4NetIOPacket & rPacket,C4NetIO * pNetIO)583 void C4Network2IO::OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO)
584 {
585 #if C4NET2IO_DUMP_LEVEL > 0
586 auto tTime = C4TimeMilliseconds::Now();
587 #endif
588 #if(C4NET2IO_DUMP_LEVEL > 1)
589 Application.InteractiveThread.ThreadLogS("OnPacket: %s status %02x %s",
590 C4TimeMilliseconds::Now().AsString().getData(),
591 rPacket.getStatus(), getNetIOName(pNetIO));
592 #endif
593 if (pNetIO == pNetIO_UDP && IsPuncherAddr(rPacket.getAddr()))
594 {
595 HandlePuncherPacket(rPacket);
596 return;
597 }
598 if (!rPacket.getSize()) return;
599 // find connection
600 C4Network2IOConnection *pConn = GetConnection(rPacket.getAddr(), pNetIO);
601 if (!pConn)
602 {
603 Application.InteractiveThread.ThreadLog("Network: could not find connection for %s packet (status %02x) from %s!", getNetIOName(pNetIO), rPacket.getStatus(), rPacket.getAddr().ToString().getData());
604 return;
605 }
606 #if(C4NET2IO_DUMP_LEVEL > 2)
607 uint32_t iFindConnectionBlocked = C4TimeMilliseconds::Now() - tTime;
608 if (iFindConnectionBlocked > 100)
609 Application.InteractiveThread.ThreadLogS("OnPacket: ... blocked %d ms for finding the connection!", iFindConnectionBlocked);
610 #endif
611 // notify
612 pConn->OnPacketReceived(rPacket.getStatus());
613 // handle packet
614 HandlePacket(rPacket, pConn, true);
615 // log time
616 #if(C4NET2IO_DUMP_LEVEL > 1)
617 uint32_t iHandlingBlocked = C4TimeMilliseconds::Now() - tTime;
618 if (iHandlingBlocked > 100)
619 Application.InteractiveThread.ThreadLogS("OnPacket: ... blocked %d ms for handling!", iHandlingBlocked);
620 #endif
621 }
622
OnError(const char * strError,C4NetIO * pNetIO)623 void C4Network2IO::OnError(const char *strError, C4NetIO *pNetIO)
624 {
625 // let's log it
626 Application.InteractiveThread.ThreadLog("Network: %s error: %s", getNetIOName(pNetIO), strError);
627 }
628
Execute(int iTimeout,pollfd *)629 bool C4Network2IO::Execute(int iTimeout, pollfd *)
630 {
631 tLastExecute = C4TimeMilliseconds::Now();
632
633 // check for timeout
634 CheckTimeout();
635
636 // ping all open connections
637 if (!Inside(tLastPing, tLastExecute - C4NetPingFreq, tLastExecute))
638 {
639 Ping();
640 tLastPing = tLastExecute;
641 }
642
643 // do statistics
644 if (!Inside(tLastStatistic, tLastExecute - C4NetStatisticsFreq, tLastExecute))
645 {
646 GenerateStatistics(tLastExecute - tLastStatistic);
647 tLastStatistic = tLastExecute;
648 }
649
650 // resources
651 ::Network.ResList.OnTimer();
652
653 // ok
654 return true;
655 }
656
GetNextTick(C4TimeMilliseconds tNow)657 C4TimeMilliseconds C4Network2IO::GetNextTick(C4TimeMilliseconds tNow)
658 {
659 return tLastExecute + C4NetTimer;
660 }
661
OnThreadEvent(C4InteractiveEventType eEvent,void * pEventData)662 void C4Network2IO::OnThreadEvent(C4InteractiveEventType eEvent, void *pEventData) // by main thread
663 {
664 switch (eEvent)
665 {
666 case Ev_Net_Conn: // got a connection
667 {
668 C4Network2IOConnection *pConn = reinterpret_cast<C4Network2IOConnection *>(pEventData);
669 // do callback
670 ::Network.OnConn(pConn);
671 // remove reference
672 pConn->DelRef();
673 }
674 break;
675
676 case Ev_Net_Disconn: // connection closed
677 {
678 C4Network2IOConnection *pConn = reinterpret_cast<C4Network2IOConnection *>(pEventData);
679 assert(pConn->isClosed());
680 // do callback
681 ::Network.OnDisconn(pConn);
682 // remove reference
683 pConn->DelRef();
684 }
685 break;
686
687 case Ev_Net_Packet: // got packet
688 {
689 NetEvPacketData *pData = reinterpret_cast<NetEvPacketData *>(pEventData);
690 // handle
691 HandlePacket(pData->Packet, pData->Conn, false);
692 // clear up
693 pData->Conn->DelRef();
694 delete pData;
695 }
696 break;
697
698 default:
699 // TODO
700 break;
701 }
702 }
703
getNetIO(C4Network2IOProtocol eProt)704 C4NetIO *C4Network2IO::getNetIO(C4Network2IOProtocol eProt) // by both
705 {
706 switch (eProt)
707 {
708 case P_UDP: return pNetIO_UDP;
709 case P_TCP: return pNetIO_TCP;
710 default: return nullptr;
711 }
712 }
713
getNetIOName(C4NetIO * pNetIO)714 const char *C4Network2IO::getNetIOName(C4NetIO *pNetIO)
715 {
716 if (!pNetIO) return "nullptr";
717 if (pNetIO == pNetIO_TCP) return "TCP";
718 if (pNetIO == pNetIO_UDP) return "UDP";
719 return "UNKNOWN";
720 }
721
getNetIOProt(C4NetIO * pNetIO)722 C4Network2IOProtocol C4Network2IO::getNetIOProt(C4NetIO *pNetIO)
723 {
724 if (!pNetIO) return P_NONE;
725 if (pNetIO == pNetIO_TCP) return P_TCP;
726 if (pNetIO == pNetIO_UDP) return P_UDP;
727 return P_NONE;
728 }
729
AddConnection(C4Network2IOConnection * pConn)730 void C4Network2IO::AddConnection(C4Network2IOConnection *pConn) // by both
731 {
732 CStdLock ConnListLock(&ConnListCSec);
733 // add reference
734 pConn->AddRef();
735 // add to list
736 pConn->pNext = pConnList; pConnList = pConn;
737 }
738
RemoveConnection(C4Network2IOConnection * pConn)739 void C4Network2IO::RemoveConnection(C4Network2IOConnection *pConn) // by both
740 {
741 CStdLock ConnListLock(&ConnListCSec);
742 // search & remove
743 if (pConnList == pConn)
744 pConnList = pConn->pNext;
745 else
746 {
747 C4Network2IOConnection *pAct;
748 for (pAct = pConnList; pAct; pAct = pAct->pNext)
749 if (pAct->pNext == pConn)
750 break;
751 if (pAct)
752 pAct->pNext = pConn->pNext;
753 else
754 return;
755 }
756 // remove reference
757 pConn->pNext = nullptr; pConn->DelRef();
758 }
759
GetConnection(const C4NetIO::addr_t & addr,C4NetIO * pNetIO)760 C4Network2IOConnection *C4Network2IO::GetConnection(const C4NetIO::addr_t &addr, C4NetIO *pNetIO) // by both
761 {
762 CStdLock ConnListLock(&ConnListCSec);
763 // search
764 for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
765 if (pConn->getNetClass() == pNetIO && pConn->getPeerAddr() == addr)
766 return pConn;
767 return nullptr;
768 }
769
GetConnectionByConnAddr(const C4NetIO::addr_t & addr,C4NetIO * pNetIO)770 C4Network2IOConnection *C4Network2IO::GetConnectionByConnAddr(const C4NetIO::addr_t &addr, C4NetIO *pNetIO) // by both
771 {
772 CStdLock ConnListLock(&ConnListCSec);
773 // search
774 for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
775 if (pConn->getNetClass() == pNetIO && pConn->getConnectAddr() == addr)
776 return pConn;
777 return nullptr;
778 }
779
GetConnectionByID(uint32_t iConnID)780 C4Network2IOConnection *C4Network2IO::GetConnectionByID(uint32_t iConnID) // by thread
781 {
782 CStdLock ConnListLock(&ConnListCSec);
783 // search
784 for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
785 if (pConn->getID() == iConnID)
786 return pConn;
787 return nullptr;
788 }
789
SetReference(C4Network2Reference * pReference)790 void C4Network2IO::SetReference(C4Network2Reference *pReference)
791 {
792 if (pRefServer)
793 pRefServer->SetReference(pReference);
794 else
795 delete pReference;
796 }
797
IsReferenceNeeded()798 bool C4Network2IO::IsReferenceNeeded()
799 {
800 return !!pRefServer;
801 }
802
doAutoAccept(const C4ClientCore & CCore,const C4Network2IOConnection & Conn)803 bool C4Network2IO::doAutoAccept(const C4ClientCore &CCore, const C4Network2IOConnection &Conn)
804 {
805 CStdLock AALock(&AutoAcceptCSec);
806 // check if connection with the given client should be allowed
807 for (AutoAccept *pAcc = pAutoAcceptList; pAcc; pAcc = pAcc->Next)
808 // core match?
809 if (CCore.getDiffLevel(pAcc->CCore) <= C4ClientCoreDL_IDMatch)
810 {
811 // check: already got another connection for this client? Peer IP must match, then.
812 for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
813 if (pConn->isAccepted() &&
814 pConn->getCCore().getDiffLevel(CCore) <= C4ClientCoreDL_IDMatch &&
815 pConn->getPeerAddr().GetHost() != Conn.getPeerAddr().GetHost())
816 return false;
817 // not found or IP matches? Let pass
818 return true;
819 }
820 return false;
821 }
822
HandlePacket(const C4NetIOPacket & rPacket,C4Network2IOConnection * pConn,bool fThread)823 bool C4Network2IO::HandlePacket(const C4NetIOPacket &rPacket, C4Network2IOConnection *pConn, bool fThread)
824 {
825 // security: add connection reference
826 if (!pConn) return false; pConn->AddRef();
827
828 // accept only PID_Conn and PID_Ping on non-accepted connections
829 if(!pConn->isHalfAccepted())
830 if(rPacket.getStatus() != PID_Conn && rPacket.getStatus() != PID_Ping && rPacket.getStatus() != PID_ConnRe)
831 return false;
832
833 // unpack packet (yet another no-idea-why-it's-needed-cast)
834 C4IDPacket Pkt; C4PacketBase &PktB = Pkt;
835 try
836 {
837 PktB.unpack(rPacket);
838 }
839 catch (StdCompiler::Exception *pExc)
840 {
841 Application.InteractiveThread.ThreadLog("Network: error: Failed to unpack packet id %02x: %s", rPacket.getStatus(), pExc->Msg.getData());
842 delete pExc;
843 #ifndef _DEBUG
844 pConn->Close();
845 #endif
846 return false;
847 }
848
849 // dump packet (network thread only)
850 #if(C4NET2IO_DUMP_LEVEL > 0)
851 if (Config.Network.PacketLogging && fThread && Pkt.getPktType() != PID_Ping && Pkt.getPktType() != PID_Pong && Pkt.getPktType() != PID_NetResData)
852 {
853 // StdStrBuf PacketDump = DecompileToBuf<StdCompilerINIWrite>(mkNamingAdaptrPacket);
854 StdStrBuf PacketHeader = FormatString("HandlePacket: %s by %s (%lu bytes, counter %d)",
855 C4TimeMilliseconds::Now().AsString().getData(),
856 pConn->getPeerAddr().ToString().getData(),
857 static_cast<unsigned long>(rPacket.getSize()), pConn->getInPacketCounter());
858 StdStrBuf Dump = DecompileToBuf<StdCompilerINIWrite>(mkNamingAdapt(Pkt, PacketHeader.getData()));
859 // Put it directly. The standard functions behind StdBuf.Format seem to choke when you pass them too much data.
860 Application.InteractiveThread.PushEvent(Ev_LogSilent, Dump.GrabPointer());
861 }
862 #endif
863
864 // search packet handling data
865 bool fSendToMainThread = false, fHandled = false;
866 for (const C4PktHandlingData *pHData = PktHandlingData; pHData->ID != PID_None; pHData++)
867 if (pHData->ID == rPacket.getStatus())
868 {
869 // correct thread?
870 if (!pHData->ProcByThread == !fThread)
871 {
872 // connection accepted?
873 if (pHData->AcceptedOnly || pConn->isAccepted() || pConn->isClosed())
874 {
875 fHandled = true;
876 #if(C4NET2IO_DUMP_LEVEL > 2)
877 C4TimeMilliseconds tStart = C4TimeMilliseconds::Now();
878 #endif
879
880 // call handler(s)
881 CallHandlers(pHData->HandlerID, &Pkt, pConn, fThread);
882
883 #if(C4NET2IO_DUMP_LEVEL > 2)
884 uint32_t iBlockedTime = C4TimeMilliseconds::Now() - tStart;
885 if (fThread && iBlockedTime > 100)
886 {
887 Application.InteractiveThread.ThreadLogS("HandlePacket: ... blocked for %u ms!", iBlockedTime);
888 }
889 #endif
890
891 }
892 }
893 // transfer to main thread?
894 else if (!pHData->ProcByThread && fThread)
895 {
896 fHandled = true;
897 fSendToMainThread = true;
898 }
899 }
900
901 // send to main thread?
902 if (fSendToMainThread)
903 {
904 // create data
905 NetEvPacketData *pEvData = new NetEvPacketData;
906 pEvData->Packet.Take(rPacket.Duplicate());
907 pEvData->Conn = pConn; pConn->AddRef();
908 // trigger event
909 if (!Application.InteractiveThread.PushEvent(Ev_Net_Packet, pEvData))
910 Application.InteractiveThread.ThreadLogS("...push event ");
911 }
912
913 // unhandled?
914 if (!fHandled && !pConn->isClosed())
915 Application.InteractiveThread.ThreadLog("Network: Unhandled packet (status %02x)", rPacket.getStatus());
916
917 // remove connection reference
918 pConn->DelRef();
919 return fHandled;
920 }
921
CallHandlers(int iHandlerID,const C4IDPacket * pPkt,C4Network2IOConnection * pConn,bool fThread)922 void C4Network2IO::CallHandlers(int iHandlerID, const C4IDPacket *pPkt, C4Network2IOConnection *pConn, bool fThread)
923 {
924 // emulate old callbacks
925 char cStatus = pPkt->getPktType();
926 const C4PacketBase *pPacket = pPkt->getPkt();
927 // this class (network thread)
928 if (iHandlerID & PH_C4Network2IO)
929 {
930 assert(fThread);
931 HandlePacket(cStatus, pPacket, pConn);
932 }
933 // main network class (main thread)
934 if (iHandlerID & PH_C4Network2)
935 {
936 assert(!fThread);
937 ::Network.HandlePacket(cStatus, pPacket, pConn);
938 }
939 // fullscreen lobby
940 if (iHandlerID & PH_C4GUIMainDlg)
941 {
942 assert(!fThread);
943 ::Network.HandleLobbyPacket(cStatus, pPacket, pConn);
944 }
945 // client list class (main thread)
946 if (iHandlerID & PH_C4Network2ClientList)
947 {
948 assert(!fThread);
949 ::Network.Clients.HandlePacket(cStatus, pPacket, pConn);
950 }
951 // player list class (main thread)
952 if (iHandlerID & PH_C4Network2Players)
953 {
954 assert(!fThread);
955 ::Network.Players.HandlePacket(cStatus, pPacket, pConn);
956 }
957 // resource list class (network thread)
958 if (iHandlerID & PH_C4Network2ResList)
959 {
960 assert(fThread);
961 ::Network.ResList.HandlePacket(cStatus, pPacket, pConn);
962 }
963 // network control (mixed)
964 if (iHandlerID & PH_C4GameControlNetwork)
965 {
966 ::Control.Network.HandlePacket(cStatus, pPacket, pConn);
967 }
968 }
969
HandlePacket(char cStatus,const C4PacketBase * pPacket,C4Network2IOConnection * pConn)970 void C4Network2IO::HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn)
971 {
972 // security
973 if (!pConn) return;
974
975 #define GETPKT(type, name) \
976 assert(pPacket); const type &name = \
977 static_cast<const type &>(*pPacket);
978
979 switch (cStatus)
980 {
981
982 case PID_Conn: // connection request
983 {
984 if (!pConn->isOpen()) break;
985 // get packet
986 GETPKT(C4PacketConn, rPkt)
987 // set connection ID
988 pConn->SetRemoteID(rPkt.getConnID());
989 // check auto-accept
990 if (doAutoAccept(rPkt.getCCore(), *pConn))
991 {
992 // send answer back
993 C4PacketConnRe pcr(true, false, "auto accept");
994 if (!pConn->Send(MkC4NetIOPacket(PID_ConnRe, pcr)))
995 pConn->Close();
996 // accept
997 pConn->SetStatus(CS_HalfAccepted);
998 pConn->SetCCore(rPkt.getCCore());
999 pConn->SetAutoAccepted();
1000 }
1001 // note that this packet will get processed by C4Network2, too (main thread)
1002 }
1003 break;
1004
1005 case PID_ConnRe: // connection request reply
1006 {
1007 if (!pConn->isOpen()) break;
1008 // conn not sent? That's fishy.
1009 // FIXME: Note this happens if the peer has exclusive connection mode on.
1010 if (!pConn->isConnSent())
1011 {
1012 pConn->Close();
1013 break;
1014 }
1015 // get packet
1016 GETPKT(C4PacketConnRe, rPkt)
1017 // auto accept connection
1018 if (rPkt.isOK())
1019 {
1020 if (pConn->isHalfAccepted() && pConn->isAutoAccepted())
1021 pConn->SetAccepted();
1022 }
1023 }
1024 break;
1025
1026 case PID_Ping:
1027 {
1028 if (!pConn->isOpen()) break;
1029 GETPKT(C4PacketPing, rPkt)
1030 // pong
1031 C4PacketPing PktPong = rPkt;
1032 pConn->Send(MkC4NetIOPacket(PID_Pong, PktPong));
1033 // remove received packets from log
1034 pConn->ClearPacketLog(rPkt.getPacketCounter());
1035 }
1036 break;
1037
1038 case PID_Pong:
1039 {
1040 if (!pConn->isOpen()) break;
1041 GETPKT(C4PacketPing, rPkt);
1042 // save
1043 pConn->SetPingTime(rPkt.getTravelTime());
1044 }
1045 break;
1046
1047 case PID_FwdReq:
1048 {
1049 GETPKT(C4PacketFwd, rPkt);
1050 HandleFwdReq(rPkt, pConn);
1051 }
1052 break;
1053
1054 case PID_Fwd:
1055 {
1056 GETPKT(C4PacketFwd, rPkt);
1057 // only received accidently?
1058 if (!rPkt.DoFwdTo(LCCore.getID())) break;
1059 // handle
1060 C4NetIOPacket Packet(rPkt.getData(), pConn->getPeerAddr());
1061 HandlePacket(Packet, pConn, true);
1062 }
1063 break;
1064
1065 case PID_PostMortem:
1066 {
1067 GETPKT(C4PacketPostMortem, rPkt);
1068 // Get connection
1069 C4Network2IOConnection *pConn = GetConnectionByID(rPkt.getConnID());
1070 if (!pConn) return;
1071 // Handle all packets
1072 uint32_t iCounter;
1073 for (iCounter = pConn->getInPacketCounter(); ; iCounter++)
1074 {
1075 // Get packet
1076 const C4NetIOPacket *pPkt = rPkt.getPacket(iCounter);
1077 if (!pPkt) break;
1078 // Handle it
1079 HandlePacket(*pPkt, pConn, true);
1080 }
1081 // Log
1082 if (iCounter > pConn->getInPacketCounter())
1083 Application.InteractiveThread.ThreadLogS("Network: Recovered %d packets", iCounter - pConn->getInPacketCounter());
1084 // Remove the connection from our list
1085 if (!pConn->isClosed())
1086 pConn->Close();
1087 RemoveConnection(pConn);
1088 }
1089 break;
1090
1091 }
1092
1093 #undef GETPKT
1094 }
1095
HandleFwdReq(const C4PacketFwd & rFwd,C4Network2IOConnection * pBy)1096 void C4Network2IO::HandleFwdReq(const C4PacketFwd &rFwd, C4Network2IOConnection *pBy)
1097 {
1098 CStdLock ConnListLock(&ConnListCSec);
1099 // init packet
1100 C4PacketFwd nFwd;
1101 nFwd.SetListType(false);
1102 // find all clients the message should be forwarded to
1103 int iClientID; C4Network2IOConnection *pConn;
1104 for (pConn = pConnList; pConn; pConn = pConn->pNext)
1105 if (pConn->isAccepted())
1106 if ((iClientID = pConn->getClientID()) >= 0)
1107 if (iClientID != pBy->getClientID())
1108 if (rFwd.DoFwdTo(iClientID) && !nFwd.DoFwdTo(iClientID))
1109 nFwd.AddClient(iClientID);
1110 // check count (hardcoded: broadcast for > 2 clients)
1111 if (nFwd.getClientCnt() <= 2)
1112 {
1113 C4NetIOPacket Pkt(rFwd.getData(), C4NetIO::addr_t());
1114 for (int i = 0; i < nFwd.getClientCnt(); i++)
1115 if ((pConn = GetMsgConnection(nFwd.getClient(i))))
1116 {
1117 pConn->Send(Pkt);
1118 pConn->DelRef();
1119 }
1120 }
1121 else
1122 {
1123 // Temporarily unlock connection list for getting broadcast lock
1124 // (might lead to deathlocks otherwise, as the lock is often taken
1125 // in the opposite order)
1126 ConnListLock.Clear();
1127
1128 BeginBroadcast();
1129 nFwd.SetData(rFwd.getData());
1130 // add all clients
1131 CStdLock ConnListLock(&ConnListCSec);
1132 for (int i = 0; i < nFwd.getClientCnt(); i++)
1133 if ((pConn = GetMsgConnection(nFwd.getClient(i))))
1134 {
1135 pConn->SetBroadcastTarget(true);
1136 pConn->DelRef();
1137 }
1138 // broadcast
1139 Broadcast(MkC4NetIOPacket(PID_Fwd, nFwd));
1140 EndBroadcast();
1141 }
1142 // doing a callback here; don't lock!
1143 ConnListLock.Clear();
1144 // forward to self?
1145 if (rFwd.DoFwdTo(LCCore.getID()))
1146 {
1147 C4NetIOPacket Packet(rFwd.getData(), pBy->getPeerAddr());
1148 HandlePacket(Packet, pBy, true);
1149 }
1150 }
1151
HandlePuncherPacket(const C4NetIOPacket & rPacket)1152 void C4Network2IO::HandlePuncherPacket(const C4NetIOPacket& rPacket)
1153 {
1154 auto pkt = C4NetpuncherPacket::Construct(rPacket);
1155 if (pkt && ::Network.HandlePuncherPacket(move(pkt), rPacket.getAddr().GetFamily()));
1156 else
1157 {
1158 assert(pNetIO_UDP);
1159 pNetIO_UDP->Close(rPacket.getAddr());
1160 }
1161 }
1162
Ping()1163 bool C4Network2IO::Ping()
1164 {
1165 bool fSuccess = true;
1166 // ping all connections
1167 for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
1168 if (pConn->isOpen())
1169 {
1170 C4PacketPing Ping(pConn->getInPacketCounter(), pConn->getOutPacketCounter());
1171 fSuccess &= pConn->Send(MkC4NetIOPacket(PID_Ping, Ping));
1172 pConn->OnPing();
1173 }
1174 return fSuccess;
1175 }
1176
CheckTimeout()1177 void C4Network2IO::CheckTimeout()
1178 {
1179 // acquire lock
1180 CStdLock ConnListLock(&ConnListCSec);
1181 // check all connections for timeout (use deletion-safe iteration method just in case)
1182 for (C4Network2IOConnection *pConn = pConnList, *pNext; pConn; pConn = pNext)
1183 {
1184 pNext = pConn->pNext;
1185 // status timeout
1186 if (!pConn->isClosed() && !pConn->isAccepted())
1187 if (difftime(time(nullptr), pConn->getTimestamp()) > C4NetAcceptTimeout)
1188 {
1189 Application.InteractiveThread.ThreadLogS("Network: connection accept timeout to %s", pConn->getPeerAddr().ToString().getData());
1190 pConn->Close();
1191 }
1192 // ping timeout
1193 if (pConn->isAccepted())
1194 if ((pConn->getLag() != -1 ? pConn->getLag() : 1000 * difftime(time(nullptr), pConn->getTimestamp()))
1195 > C4NetPingTimeout)
1196 {
1197 Application.InteractiveThread.ThreadLogS("%d %d %d", (int)pConn->getLag(), (int)time(nullptr), (int)pConn->getTimestamp());
1198 Application.InteractiveThread.ThreadLogS("Network: ping timeout to %s", pConn->getPeerAddr().ToString().getData());
1199 pConn->Close();
1200 }
1201 // delayed connection removal
1202 if (pConn->isClosed())
1203 if (difftime(time(nullptr), pConn->getTimestamp()) > C4NetAcceptTimeout)
1204 RemoveConnection(pConn);
1205 }
1206 }
1207
GenerateStatistics(int iInterval)1208 void C4Network2IO::GenerateStatistics(int iInterval)
1209 {
1210 int iTCPIRateSum = 0, iTCPORateSum = 0, iUDPIRateSum = 0, iUDPORateSum = 0;
1211
1212 // acquire lock, get connection statistics
1213 CStdLock ConnListLock(&ConnListCSec);
1214 for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
1215 if (pConn->isOpen())
1216 {
1217 bool fTCP = pConn->getNetClass() == pNetIO_TCP;
1218 pConn->DoStatistics(iInterval, fTCP ? &iTCPIRateSum : &iUDPIRateSum,
1219 fTCP ? &iTCPORateSum : &iUDPORateSum);
1220 }
1221 ConnListLock.Clear();
1222
1223 // get broadcast statistics
1224 int inTCPBCRate = 0, inUDPBCRate = 0;
1225 if (pNetIO_TCP) pNetIO_TCP->GetStatistic(&inTCPBCRate);
1226 if (pNetIO_UDP) pNetIO_UDP->GetStatistic(&inUDPBCRate);
1227
1228 // normalize everything
1229 iTCPIRateSum = iTCPIRateSum * 1000 / iInterval;
1230 iTCPORateSum = iTCPORateSum * 1000 / iInterval;
1231 iUDPIRateSum = iUDPIRateSum * 1000 / iInterval;
1232 iUDPORateSum = iUDPORateSum * 1000 / iInterval;
1233 inTCPBCRate = inTCPBCRate * 1000 / iInterval;
1234 inUDPBCRate = inUDPBCRate * 1000 / iInterval;
1235
1236 // clear
1237 if (pNetIO_TCP) pNetIO_TCP->ClearStatistic();
1238 if (pNetIO_UDP) pNetIO_UDP->ClearStatistic();
1239
1240 // save back
1241 iTCPIRate = iTCPIRateSum; iTCPORate = iTCPORateSum; iTCPBCRate = inTCPBCRate;
1242 iUDPIRate = iUDPIRateSum; iUDPORate = iUDPORateSum; iUDPBCRate = inUDPBCRate;
1243 }
1244
SendConnPackets()1245 void C4Network2IO::SendConnPackets()
1246 {
1247 CStdLock ConnListLock(&ConnListCSec);
1248
1249 // exlusive conn?
1250 if (fExclusiveConn)
1251 // find a live connection
1252 for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
1253 if (pConn->isAccepted() || (!pConn->isClosed() && pConn->isConnSent()))
1254 // do not sent additional conn packets - no other connection should succeed
1255 return;
1256
1257 // sent pending welcome packet(s)
1258 for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
1259 if (pConn->isOpen() && !pConn->isConnSent())
1260 {
1261 // make packet
1262 CStdLock LCCoreLock(&LCCoreCSec);
1263 C4NetIOPacket Pkt = MkC4NetIOPacket(PID_Conn, C4PacketConn(LCCore, pConn->getID(), pConn->getPassword()));
1264 LCCoreLock.Clear();
1265 // send
1266 if (!pConn->Send(Pkt))
1267 pConn->Close();
1268 else
1269 {
1270 // set flag
1271 pConn->SetConnSent();
1272 // only one conn packet at a time
1273 if (fExclusiveConn)
1274 return;
1275 }
1276 }
1277
1278 }
1279
1280 // *** C4Network2IOConnection
1281
C4Network2IOConnection()1282 C4Network2IOConnection::C4Network2IOConnection()
1283 : iID(~0), iRemoteID(~0),
1284
1285 tLastPing(C4TimeMilliseconds::NegativeInfinity),
1286 tLastPong(C4TimeMilliseconds::NegativeInfinity),
1287
1288 iRefCnt(0)
1289 {
1290 }
1291
~C4Network2IOConnection()1292 C4Network2IOConnection::~C4Network2IOConnection()
1293 {
1294 assert(!iRefCnt);
1295 // connection needs to be closed?
1296 if (pNetClass && !isClosed()) Close();
1297 // clear the packet log
1298 ClearPacketLog();
1299 }
1300
getLag() const1301 int C4Network2IOConnection::getLag() const
1302 {
1303 if (iPingTime != -1)
1304 {
1305 // Last ping not answered yet?
1306 if(tLastPing > tLastPong)
1307 {
1308 int iPingLag = C4TimeMilliseconds::Now() - tLastPing;
1309 // Use it for lag measurement once it's larger then the last ping time
1310 // (the ping time won't be better than this anyway once the pong's here)
1311 return std::max(iPingLag, iPingTime);
1312 }
1313 }
1314 // Last ping result
1315 return iPingTime;
1316 }
1317
Set(C4NetIO * pnNetClass,C4Network2IOProtocol enProt,const C4NetIO::addr_t & nPeerAddr,const C4NetIO::addr_t & nConnectAddr,C4Network2IOConnStatus nStatus,const char * szPassword,uint32_t inID)1318 void C4Network2IOConnection::Set(C4NetIO *pnNetClass, C4Network2IOProtocol enProt, const C4NetIO::addr_t &nPeerAddr, const C4NetIO::addr_t &nConnectAddr, C4Network2IOConnStatus nStatus, const char *szPassword, uint32_t inID)
1319 {
1320 // save data
1321 pNetClass = pnNetClass; eProt = enProt;
1322 PeerAddr = nPeerAddr; ConnectAddr = nConnectAddr;
1323 Status = nStatus;
1324 Password = szPassword;
1325 iID = inID;
1326 // initialize
1327 fBroadcastTarget = false;
1328 iTimestamp = time(nullptr); iPingTime = -1;
1329 }
1330
SetRemoteID(uint32_t inRemoteID)1331 void C4Network2IOConnection::SetRemoteID(uint32_t inRemoteID)
1332 {
1333 iRemoteID = inRemoteID;
1334 }
1335
SetPeerAddr(const C4NetIO::addr_t & nPeerAddr)1336 void C4Network2IOConnection::SetPeerAddr(const C4NetIO::addr_t &nPeerAddr)
1337 {
1338 // just do it
1339 PeerAddr = nPeerAddr;
1340 }
1341
OnPing()1342 void C4Network2IOConnection::OnPing()
1343 {
1344 // Still no pong for the last ping?
1345 if (tLastPong < tLastPing)
1346 return;
1347
1348 // Save time
1349 tLastPing = C4TimeMilliseconds::Now();
1350 }
1351
SetPingTime(int inPingTime)1352 void C4Network2IOConnection::SetPingTime(int inPingTime)
1353 {
1354 // save it
1355 iPingTime = inPingTime;
1356 // pong received - save timestamp
1357 tLastPong = C4TimeMilliseconds::Now();
1358 }
1359
SetStatus(C4Network2IOConnStatus nStatus)1360 void C4Network2IOConnection::SetStatus(C4Network2IOConnStatus nStatus)
1361 {
1362 if (nStatus != Status)
1363 {
1364 // Connection can't return from these
1365 assert(!isClosed());
1366 // set status
1367 Status = nStatus;
1368 // reset timestamp for connect/accept/close
1369 if (Status == CS_Connect || Status == CS_Connected || Status == CS_Accepted || Status == CS_Closed)
1370 iTimestamp = time(nullptr);
1371 }
1372 }
1373
SetAutoAccepted()1374 void C4Network2IOConnection::SetAutoAccepted()
1375 {
1376 fAutoAccept = true;
1377 }
1378
OnPacketReceived(uint8_t iPacketType)1379 void C4Network2IOConnection::OnPacketReceived(uint8_t iPacketType)
1380 {
1381 // Just count them
1382 if (iPacketType >= PID_PacketLogStart)
1383 iInPacketCounter++;
1384 }
1385
ClearPacketLog(uint32_t iUntilID)1386 void C4Network2IOConnection::ClearPacketLog(uint32_t iUntilID)
1387 {
1388 // Search position of first packet to delete
1389 PacketLogEntry *pPos, *pPrev = nullptr;
1390 for (pPos = pPacketLog; pPos; pPrev = pPos, pPos = pPos->Next)
1391 if (pPos->Number < iUntilID)
1392 break;
1393 if (pPos)
1394 {
1395 // Remove packets from list
1396 (pPrev ? pPrev->Next : pPacketLog) = nullptr;
1397 // Delete everything
1398 while (pPos)
1399 {
1400 PacketLogEntry *pDelete = pPos;
1401 pPos = pPos->Next;
1402 delete pDelete;
1403 }
1404 }
1405 }
1406
CreatePostMortem(C4PacketPostMortem * pPkt)1407 bool C4Network2IOConnection::CreatePostMortem(C4PacketPostMortem *pPkt)
1408 {
1409 // Security
1410 if (!pPkt) return false;
1411 CStdLock PacketLogLock(&PacketLogCSec);
1412 // Nothing to do?
1413 if (!pPacketLog) return false;
1414 // Already created?
1415 if (fPostMortemSent) return false;
1416 // Set connection ID and packet counter
1417 pPkt->SetConnID(iRemoteID);
1418 pPkt->SetPacketCounter(iOutPacketCounter);
1419 // Add packets
1420 for (PacketLogEntry *pEntry = pPacketLog; pEntry; pEntry = pEntry->Next)
1421 pPkt->Add(pEntry->Pkt);
1422 // Okay
1423 fPostMortemSent = true;
1424 return true;
1425 }
1426
SetCCore(const C4ClientCore & nCCore)1427 void C4Network2IOConnection::SetCCore(const C4ClientCore &nCCore)
1428 {
1429 CStdLock CCoreLock(&CCoreCSec);
1430 CCore = nCCore;
1431 }
1432
Connect()1433 bool C4Network2IOConnection::Connect()
1434 {
1435 if (!pNetClass) return false;
1436 // try connect
1437 return pNetClass->Connect(ConnectAddr);
1438 }
1439
Close()1440 void C4Network2IOConnection::Close()
1441 {
1442 if (!pNetClass || isClosed()) return;
1443 // set status
1444 SetStatus(CS_Closed);
1445 // close
1446 pNetClass->Close(PeerAddr);
1447 }
1448
Send(const C4NetIOPacket & rPkt)1449 bool C4Network2IOConnection::Send(const C4NetIOPacket &rPkt)
1450 {
1451 // some packets shouldn't go into the log
1452 if (rPkt.getStatus() < PID_PacketLogStart)
1453 {
1454 assert(isOpen());
1455 C4NetIOPacket Copy(rPkt);
1456 Copy.SetAddr(PeerAddr);
1457 return pNetClass->Send(Copy);
1458 }
1459 CStdLock PacketLogLock(&PacketLogCSec);
1460 // create log entry
1461 PacketLogEntry *pLogEntry = new PacketLogEntry();
1462 pLogEntry->Number = iOutPacketCounter++;
1463 pLogEntry->Pkt = rPkt;
1464 pLogEntry->Next = pPacketLog;
1465 pPacketLog = pLogEntry;
1466 // set address
1467 pLogEntry->Pkt.SetAddr(PeerAddr);
1468 // closed? No sweat, post mortem will reroute it later.
1469 if (!isOpen())
1470 {
1471 // post mortem already sent? This shouldn't happen
1472 if (fPostMortemSent) { assert(false); return false; }
1473 // okay then
1474 return true;
1475 }
1476 // send
1477 bool fSuccess = pNetClass->Send(pLogEntry->Pkt);
1478 if (fSuccess)
1479 assert(!fPostMortemSent);
1480 else {
1481 // Not being able to send a packet is actually a big deal,
1482 // as this means that we will have hole in the packet
1483 // order. Better close the connection - post mortem should
1484 // ideally sort everything out from here.
1485 LogF("Network: Fatal: Send failed (%s)", pNetClass->GetError());
1486 pNetClass->ResetError();
1487 Close();
1488 }
1489 return fSuccess;
1490 }
1491
SetBroadcastTarget(bool fSet)1492 void C4Network2IOConnection::SetBroadcastTarget(bool fSet)
1493 {
1494 // Note that each thread will have to make sure that this flag won't be
1495 // changed until Broadcast() is called. See C4Network2IO::BroadcastCSec.
1496 pNetClass->SetBroadcast(PeerAddr, fSet);
1497 fBroadcastTarget = fSet;
1498 }
1499
DoStatistics(int iInterval,int * pIRateSum,int * pORateSum)1500 void C4Network2IOConnection::DoStatistics(int iInterval, int *pIRateSum, int *pORateSum)
1501 {
1502 // get C4NetIO statistics
1503 int inIRate, inORate, inLoss;
1504 if (!isOpen() || !pNetClass->GetConnStatistic(PeerAddr, &inIRate, &inORate, &inLoss))
1505 {
1506 iIRate = iORate = iPacketLoss = 0;
1507 return;
1508 }
1509 // normalize
1510 inIRate = inIRate * 1000 / iInterval;
1511 inORate = inORate * 1000 / iInterval;
1512 // set
1513 iIRate = inIRate; iORate = inORate; iPacketLoss = inLoss;
1514 // sum up
1515 if (pIRateSum) *pIRateSum += iIRate;
1516 if (pORateSum) *pORateSum += iORate;
1517 }
1518
AddRef()1519 void C4Network2IOConnection::AddRef()
1520 {
1521 ++iRefCnt;
1522 }
1523
DelRef()1524 void C4Network2IOConnection::DelRef()
1525 {
1526 if (--iRefCnt == 0)
1527 delete this;
1528 }
1529
1530
1531 // *** C4PacketPostMortem
1532
C4PacketPostMortem()1533 C4PacketPostMortem::C4PacketPostMortem()
1534 : iConnID(~0),
1535 iPacketCounter(~0)
1536 {
1537
1538 }
1539
~C4PacketPostMortem()1540 C4PacketPostMortem::~C4PacketPostMortem()
1541 {
1542 while (pPackets)
1543 {
1544 PacketLink *pDelete = pPackets;
1545 pPackets = pPackets->Next;
1546 delete pDelete;
1547 }
1548 iPacketCount = 0;
1549 }
1550
getPacket(uint32_t iNumber) const1551 const C4NetIOPacket *C4PacketPostMortem::getPacket(uint32_t iNumber) const
1552 {
1553 // Security
1554 if (!Inside(iNumber, iPacketCounter - iPacketCount, iPacketCounter - 1))
1555 return nullptr;
1556 // Calculate position in list
1557 iNumber = iNumber + iPacketCount - iPacketCounter;
1558 // Search for the packet with the given number
1559 PacketLink *pLink = pPackets;
1560 for (; pLink && iNumber; iNumber--)
1561 pLink = pLink->Next;
1562 // Not found?
1563 return pLink ? &pLink->Pkt : nullptr;
1564 }
1565
SetPacketCounter(uint32_t inPacketCounter)1566 void C4PacketPostMortem::SetPacketCounter(uint32_t inPacketCounter)
1567 {
1568 iPacketCounter = inPacketCounter;
1569 }
1570
Add(const C4NetIOPacket & rPkt)1571 void C4PacketPostMortem::Add(const C4NetIOPacket &rPkt)
1572 {
1573 // Add to head of list (reverse order)
1574 PacketLink *pLink = new PacketLink();
1575 pLink->Pkt = rPkt;
1576 pLink->Next = pPackets;
1577 pPackets = pLink;
1578 iPacketCount++;
1579 }
1580
CompileFunc(StdCompiler * pComp)1581 void C4PacketPostMortem::CompileFunc(StdCompiler *pComp)
1582 {
1583 bool deserializing = pComp->isDeserializer();
1584
1585 // Connection ID, packet number and packet count
1586 pComp->Value(mkNamingAdapt(iConnID, "ConnID"));
1587 pComp->Value(mkNamingAdapt(iPacketCounter, "PacketCounter"));
1588 pComp->Value(mkNamingAdapt(iPacketCount, "PacketCount"));
1589
1590 // Packets
1591 if (deserializing)
1592 {
1593 // Read packets
1594 for (uint32_t i = 0; i < iPacketCount; i++)
1595 {
1596 // Create list entry
1597 PacketLink *pLink = new PacketLink();
1598 pLink->Next = pPackets;
1599 pPackets = pLink;
1600 // Compile data
1601 pComp->Value(mkNamingAdapt(pLink->Pkt, "PacketData"));
1602 }
1603 // Reverse order
1604 PacketLink *pPackets2 = pPackets;
1605 pPackets = nullptr;
1606 while (pPackets2)
1607 {
1608 // Get link
1609 PacketLink *pLink = pPackets2;
1610 pPackets2 = pLink->Next;
1611 // Readd to list
1612 pLink->Next = pPackets;
1613 pPackets = pLink;
1614 }
1615 }
1616 else
1617 {
1618 // Write packets
1619 for (PacketLink *pLink = pPackets; pLink; pLink = pLink->Next)
1620 pComp->Value(mkNamingAdapt(pLink->Pkt, "PacketData"));
1621 }
1622 }
1623