OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4Network2IO.cpp
Go to the documentation of this file.
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"
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
33 {
36 };
37 
38 // compile options
39 #define C4NET2IO_DUMP_LEVEL 1
40 
41 // *** C4Network2IO
42 
44  : tLastExecute(0), tLastPing(0), tLastStatistic(0)
45 {
46 }
47 
49 {
50  Clear();
51 }
52 
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
62 
63  // init event callback
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);
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
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
171  Thread.AddProc(this);
172 
173  // ok
174  return true;
175 }
176 
177 void C4Network2IO::Clear() // by main thread
178 {
179  // process remaining events
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 
214 {
215  CStdLock LCCoreLock(&LCCoreCSec);
216  LCCore = nCCore;
217 }
218 
220 {
221  if (pNetIO_UDP) return pNetIO_UDP;
222  if (pNetIO_TCP) return pNetIO_TCP;
223  return nullptr;
224 }
225 
227 {
228  if (pNetIO_TCP) return pNetIO_TCP;
229  if (pNetIO_UDP) return pNetIO_UDP;
230  return nullptr;
231 }
232 
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
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 
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  {
273  }
274  }
275 }
276 
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 
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 
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 
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 
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 
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 
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 
374 void C4Network2IO::BeginBroadcast(bool fSelectAll)
375 {
376  // lock
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 
386 {
387  // unlock
389 }
390 
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 
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 
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 
445 {
446  // UDP must be initialized
447  if (!pNetIO_UDP)
448  return false;
449  // save address
450  switch (nPuncherAddr.GetFamily())
451  {
453  PuncherAddrIPv4 = nPuncherAddr;
454  break;
456  PuncherAddrIPv6 = nPuncherAddr;
457  break;
459  assert(!"Unexpected address family");
460  }
461  // let's punch
462  return pNetIO_UDP->Connect(nPuncherAddr);
463 }
464 
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 
474 {
475  if (!pNetIO_UDP) return;
478  else if (family == C4NetIO::HostAddress::IPv6 && !PuncherAddrIPv6.IsNull())
480 }
481 
483 {
484  return (!PuncherAddrIPv4.IsNull() && PuncherAddrIPv4 == addr)
485  || (!PuncherAddrIPv6.IsNull() && PuncherAddrIPv6 == addr);
486 }
487 
488 // C4NetIO interface
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 
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)
552  else
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
580  // don't remove connection from list - wait for postmortem or timeout
581 }
582 
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 
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 
629 bool C4Network2IO::Execute(int iTimeout, pollfd *)
630 {
632 
633  // check for timeout
634  CheckTimeout();
635 
636  // ping all open connections
638  {
639  Ping();
641  }
642 
643  // do statistics
645  {
648  }
649 
650  // resources
652 
653  // ok
654  return true;
655 }
656 
658 {
659  return tLastExecute + C4NetTimer;
660 }
661 
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 
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 
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 
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 
731 {
732  CStdLock ConnListLock(&ConnListCSec);
733  // add reference
734  pConn->AddRef();
735  // add to list
736  pConn->pNext = pConnList; pConnList = pConn;
737 }
738 
740 {
741  CStdLock ConnListLock(&ConnListCSec);
742  // search & remove
743  if (pConnList == pConn)
744  pConnList = pConn->pNext;
745  else
746  {
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 
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 
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 
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 
791 {
792  if (pRefServer)
793  pRefServer->SetReference(pReference);
794  else
795  delete pReference;
796 }
797 
799 {
800  return !!pRefServer;
801 }
802 
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 
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.
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)
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
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 
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 
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 
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
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 
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 
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 
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 
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
1239 
1240  // save back
1241  iTCPIRate = iTCPIRateSum; iTCPORate = iTCPORateSum; iTCPBCRate = inTCPBCRate;
1242  iUDPIRate = iUDPIRateSum; iUDPORate = iUDPORateSum; iUDPBCRate = inUDPBCRate;
1243 }
1244 
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 
1283  : iID(~0), iRemoteID(~0),
1284 
1285  tLastPing(C4TimeMilliseconds::NegativeInfinity),
1286  tLastPong(C4TimeMilliseconds::NegativeInfinity),
1287 
1288  iRefCnt(0)
1289 {
1290 }
1291 
1293 {
1294  assert(!iRefCnt);
1295  // connection needs to be closed?
1296  if (pNetClass && !isClosed()) Close();
1297  // clear the packet log
1298  ClearPacketLog();
1299 }
1300 
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 
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 
1331 void C4Network2IOConnection::SetRemoteID(uint32_t inRemoteID)
1332 {
1333  iRemoteID = inRemoteID;
1334 }
1335 
1337 {
1338  // just do it
1339  PeerAddr = nPeerAddr;
1340 }
1341 
1343 {
1344  // Still no pong for the last ping?
1345  if (tLastPong < tLastPing)
1346  return;
1347 
1348  // Save time
1350 }
1351 
1353 {
1354  // save it
1355  iPingTime = inPingTime;
1356  // pong received - save timestamp
1358 }
1359 
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
1370  iTimestamp = time(nullptr);
1371  }
1372 }
1373 
1375 {
1376  fAutoAccept = true;
1377 }
1378 
1380 {
1381  // Just count them
1382  if (iPacketType >= PID_PacketLogStart)
1383  iInPacketCounter++;
1384 }
1385 
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 
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);
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 
1428 {
1429  CStdLock CCoreLock(&CCoreCSec);
1430  CCore = nCCore;
1431 }
1432 
1434 {
1435  if (!pNetClass) return false;
1436  // try connect
1437  return pNetClass->Connect(ConnectAddr);
1438 }
1439 
1441 {
1442  if (!pNetClass || isClosed()) return;
1443  // set status
1445  // close
1447 }
1448 
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 
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.
1497  fBroadcastTarget = fSet;
1498 }
1499 
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 
1520 {
1521  ++iRefCnt;
1522 }
1523 
1525 {
1526  if (--iRefCnt == 0)
1527  delete this;
1528 }
1529 
1530 
1531 // *** C4PacketPostMortem
1532 
1534  : iConnID(~0),
1535  iPacketCounter(~0)
1536 {
1537 
1538 }
1539 
1541 {
1542  while (pPackets)
1543  {
1544  PacketLink *pDelete = pPackets;
1545  pPackets = pPackets->Next;
1546  delete pDelete;
1547  }
1548  iPacketCount = 0;
1549 }
1550 
1551 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 
1566 void C4PacketPostMortem::SetPacketCounter(uint32_t inPacketCounter)
1567 {
1568  iPacketCounter = inPacketCounter;
1569 }
1570 
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 
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 }
bool fExclusiveConn
Definition: C4Network2IO.h:90
const char * getData() const
Definition: StdBuf.h:442
C4NetIO * pNetIO_UDP
Definition: C4Network2IO.h:55
void ResetError()
Definition: C4NetIO.h:285
int getConnectionCount()
void SetReference(class C4Network2Reference *pReference)
CStdCSec BroadcastCSec
Definition: C4Network2IO.h:72
C4Network2IOConnection * GetConnection(const C4NetIO::addr_t &addr, C4NetIO *pNetIO)
C4NetIOPacket PackTo(const C4NetIO::addr_t &) const
C4Config Config
Definition: C4Config.cpp:833
void OnDisconn(const C4NetIO::addr_t &addr, C4NetIO *pNetIO, const char *szReason) override
void Take(void *pnData, size_t inSize)
Definition: StdBuf.h:124
void SetPacketCounter(uint32_t iPacketCounter)
void HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn)
bool HandlePacket(const C4NetIOPacket &rPacket, C4Network2IOConnection *pConn, bool fThread)
~C4Network2IO() override
void OnDisconn(C4Network2IOConnection *pConn)
Definition: C4Network2.cpp:835
void SendPuncherPacket(const C4NetpuncherPacket &, C4NetIO::HostAddress::AddressFamily family)
const int C4NetAcceptTimeout
Definition: C4Network2IO.h:37
bool ThreadLogS(const char *szMessage,...) GNUC_FORMAT_ATTRIBUTE_O
void AddAutoAccept(const C4ClientCore &CCore)
void SetCCore(const C4ClientCore &nCCore)
int32_t PacketLogging
Definition: C4Config.h:168
CStdCSec ConnListCSec
Definition: C4Network2IO.h:72
C4Network2IOConnection * GetMsgConnection(int iClientID)
bool SendMsgToClient(C4NetIOPacket &rPkt, int iClient)
void OnPacketReceived(uint8_t iPacketType)
C4Network2IOProtocol eProt
Definition: C4Network2IO.h:224
void OnPuncherConnect(C4NetIO::addr_t addr)
StdStrBuf ToString(int flags=0) const
Definition: C4NetIO.cpp:599
bool Connect(const C4NetIO::addr_t &addr, C4Network2IOProtocol eProt, const C4ClientCore &nCCore, const char *szPassword=nullptr)
C4Network2IOConnection * GetConnectionByConnAddr(const C4NetIO::addr_t &addr, C4NetIO *pNetIO)
C4Network2IOConnection * Conn
virtual const char * GetError() const
Definition: C4NetIO.h:284
bool Send(const C4NetIOPacket &rPkt)
C4Network2IOProtocol
Definition: C4Network2IO.h:29
const int32_t C4ClientCoreDL_IDMatch
Definition: C4Client.h:30
uint32_t getInPacketCounter() const
Definition: C4Network2IO.h:281
bool PushEvent(C4InteractiveEventType eEventType, void *pData=nullptr)
void OnPacket(const C4NetIOPacket &rPacket, C4NetIO *pNetIO) override
static std::unique_ptr< C4NetpuncherPacket > Construct(const C4NetIOPacket &rpack)
void CompileFunc(StdCompiler *pComp) override
void SetExclusiveConnMode(bool fExclusiveConn)
C4Network2IOConnection * pNext
Definition: C4Network2IO.h:256
bool IsReferenceNeeded()
virtual void SetCallback(CBClass *pnCallback)=0
const C4PktHandlingData PktHandlingData[]
Definition: C4Packet2.cpp:50
C4PacketPostMortem()
const C4NetIO::addr_t & getPeerAddr() const
Definition: C4Network2IO.h:264
C4NetIO * MsgIO()
bool isHalfAccepted() const
Definition: C4Network2IO.h:286
void ClearCallback(C4InteractiveEventType eEvent, Callback *pnNetworkCallback)
size_t getSize() const
Definition: StdBuf.h:101
void Add(const C4NetIOPacket &rPkt)
class C4Network2RefServer * pRefServer
Definition: C4Network2IO.h:61
const int C4NetPingFreq
Definition: C4Network2IO.h:35
bool ThreadLog(const char *szMessage,...) GNUC_FORMAT_ATTRIBUTE_O
C4PacketType ID
Definition: C4PacketBase.h:201
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
void HandlePacket(char cStatus, const C4PacketBase *pPacket, class C4Network2IOConnection *pConn)
C4NetIO::addr_t PeerAddr
Definition: C4Network2IO.h:225
void SetPingTime(int iPingTime)
void HandleLobbyPacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn)
Definition: C4Network2.cpp:962
CStdCSec LCCoreCSec
Definition: C4Network2IO.h:68
const C4NetIO::addr_t & getAddr() const
Definition: C4NetIO.h:315
void ClearAutoAccept()
void Punch(const C4NetIO::addr_t &)
C4TimeMilliseconds tLastExecute
Definition: C4Network2IO.h:93
class C4NetIO * pNetClass
Definition: C4Network2IO.h:223
C4ClientCore LCCore
Definition: C4Network2IO.h:67
bool LogSilentF(const char *strMessage,...)
Definition: C4Log.cpp:270
void AddClient(int32_t iClient)
Definition: C4Packet2.cpp:459
C4Network2 Network
Definition: C4Globals.cpp:53
int32_t getClient(int32_t i) const
Definition: C4Network2IO.h:402
C4Network2IOProtocol getNetIOProt(C4NetIO *pNetIO)
std::atomic_long iRefCnt
Definition: C4Network2IO.h:259
bool isAccepted() const
Definition: C4Network2IO.h:287
void Clear()
Definition: StdSync.h:178
bool doAutoAccept(const C4ClientCore &CCore, const C4Network2IOConnection &Conn)
uint32_t iNextConnID
Definition: C4Network2IO.h:75
void AddConnection(C4Network2IOConnection *pConn)
C4GameControl Control
void SetRemoteID(uint32_t iRemoteID)
void SetDiscoverable(bool fnEnabled)
C4NetIOPacket MkC4NetIOPacket(char cStatus, const class C4PacketBase &Pkt, const C4NetIO::addr_t &addr=C4NetIO::addr_t())
Definition: C4PacketBase.h:40
int getClientID() const
Definition: C4Network2IO.h:271
virtual bool GetConnStatistic(const addr_t &addr, int *pIRate, int *pORate, int *pLoss)=0
bool OnConn(const C4NetIO::addr_t &addr, const C4NetIO::addr_t &AddrConnect, const C4NetIO::addr_t *pOwnAddr, C4NetIO *pNetIO) override
C4Network2IOConnection * GetConnectionByID(uint32_t iConnID)
void RemoveConnection(C4Network2IOConnection *pConn)
HostAddress GetHost() const
Definition: C4NetIO.h:154
C4NetIO * getNetIO(C4Network2IOProtocol eProt)
C4TimeMilliseconds tLastPing
Definition: C4Network2IO.h:234
void SetBroadcastTarget(bool fSet)
void SetListType(bool fnNegativeList)
Definition: C4Packet2.cpp:454
bool Broadcast(const C4NetIOPacket &rPkt)
void Set(C4NetIO *pnNetClass, C4Network2IOProtocol eProt, const C4NetIO::addr_t &nPeerAddr, const C4NetIO::addr_t &nConnectAddr, C4Network2IOConnStatus nStatus, const char *szPassword, uint32_t iID)
C4ConfigNetwork Network
Definition: C4Config.h:255
CStdCSec AutoAcceptCSec
Definition: C4Network2IO.h:87
#define GETPKT(type, name)
void SetAcceptMode(bool fAcceptAll)
bool isConnecting() const
Definition: C4Network2IO.h:284
bool fAllowConnect
Definition: C4Network2IO.h:78
const int C4NetPingTimeout
Definition: C4Network2IO.h:38
virtual void ClearStatistic()=0
bool LogFatal(const char *szMessage)
Definition: C4Log.cpp:237
C4NetIO::addr_t PuncherAddrIPv4
Definition: C4Network2IO.h:102
C4InteractiveEventType
AddressFamily GetFamily() const
Definition: C4NetIO.cpp:520
int32_t getID() const
Definition: C4Client.h:57
void SendConnPackets()
void HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn)
void Value(const T &rStruct)
Definition: StdCompiler.h:161
EndpointAddress addr_t
Definition: C4NetIO.h:211
void OnConn(C4Network2IOConnection *pConn)
Definition: C4Network2.cpp:827
bool BroadcastMsg(const C4NetIOPacket &rPkt)
C4Network2ResList ResList
Definition: C4Network2.h:113
bool AddProc(StdSchedulerProc *pProc)
int32_t getDiffLevel(const C4ClientCore &CCore2) const
Definition: C4Client.cpp:75
C4Network2IOConnection * pConnList
Definition: C4Network2IO.h:71
C4NetIO * DataIO()
void SetLocalCCore(const C4ClientCore &CCore)
void RemoveProc(StdSchedulerProc *pProc)
virtual bool Close()=0
virtual bool isDeserializer()
Definition: StdCompiler.h:53
void OnThreadEvent(C4InteractiveEventType eEvent, void *pEventData) override
bool Init(uint16_t iPort=addr_t::IPPORT_NONE) override
Definition: C4NetIO.cpp:806
virtual bool Send(const class C4NetIOPacket &rPacket)=0
C4NetIO * pNetIO_TCP
Definition: C4Network2IO.h:55
void BeginBroadcast(bool fSelectAll=false)
virtual void OnError(const char *strError, C4NetIO *pNetIO)
C4Network2ClientList Clients
Definition: C4Network2.h:116
virtual void Leave()
Definition: StdSync.h:151
void SetCallback(C4InteractiveEventType eEvent, Callback *pnNetworkCallback)
int32_t getClientCnt() const
Definition: C4Network2IO.h:403
C4NetIO::addr_t PuncherAddrIPv6
Definition: C4Network2IO.h:102
void EndBroadcast()
void AddMapping(enum C4Network2IOProtocol protocol, uint16_t intport, uint16_t extport)
virtual void Enter()
Definition: StdSync.h:150
void GenerateStatistics(int iInterval)
C4GameControlNetwork Network
Definition: C4GameControl.h:67
void RemoveAutoAccept(const C4ClientCore &CCore)
class C4Network2UPnP * UPnPMgr
Definition: C4Network2IO.h:64
StdCopyStrBuf Password
Definition: C4Network2IO.h:240
const C4NetIOPacket * getPacket(uint32_t iNumber) const
const int C4NetTimer
Definition: C4Network2IO.h:34
C4Network2IOConnStatus Status
Definition: C4Network2IO.h:228
PacketLogEntry * pPacketLog
Definition: C4Network2IO.h:252
uint8_t getStatus() const
Definition: C4NetIO.h:317
C4Network2IOConnection * GetDataConnection(int iClientID)
class C4Network2IODiscover * pNetIODiscover
Definition: C4Network2IO.h:58
C4Network2IOConnStatus
Definition: C4Network2IO.h:203
C4Network2Players Players
Definition: C4Network2.h:119
void HandlePacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn)
void SetConnID(uint32_t inConnID)
Definition: C4Network2IO.h:434
C4TimeMilliseconds GetNextTick(C4TimeMilliseconds tNow) override
bool Log(const char *szMessage)
Definition: C4Log.cpp:202
C4TimeMilliseconds tLastPing
Definition: C4Network2IO.h:94
virtual bool Init(uint16_t iPort=addr_t::IPPORT_NONE)=0
bool DoFwdTo(int32_t iClient) const
Definition: C4Packet2.cpp:441
Definition: C4Network2IO.h:246
void DoStatistics(int iInterval, int *pIRateSum, int *pORateSum)
bool IsNull() const
Definition: C4NetIO.cpp:503
bool isConnSent() const
Definition: C4Network2IO.h:279
virtual bool GetStatistic(int *pBroadcastRate)=0
~C4PacketPostMortem() override
C4NetIO::addr_t ConnectAddr
Definition: C4Network2IO.h:225
void SetStatus(C4Network2IOConnStatus nStatus)
C4TimeMilliseconds tLastStatistic
Definition: C4Network2IO.h:97
void CallHandlers(int iHandlers, const class C4IDPacket *pPacket, C4Network2IOConnection *pConn, bool fThread)
bool CreatePostMortem(class C4PacketPostMortem *pPkt)
bool Init(int16_t iPortTCP, int16_t iPortUDP, int16_t iPortDiscovery=-1, int16_t iPortRefServer=-1, bool fBroadcast=false, bool enable_upnp=true)
void HandleFwdReq(const class C4PacketFwd &rFwd, C4Network2IOConnection *pBy)
const StdCopyBuf & getData() const
Definition: C4Network2IO.h:400
void SetData(const StdBuf &Pkt)
Definition: C4Packet2.cpp:449
const int C4NetStatisticsFreq
Definition: C4Network2IO.h:36
bool Init(uint16_t iPort=C4NetIO::addr_t::IPPORT_NONE) override
bool HandlePuncherPacket(C4NetpuncherPacket::uptr, C4NetIO::HostAddress::AddressFamily family)
Definition: C4Network2.cpp:971
bool Execute(int iTimeout, pollfd *) override
C4NetIOPacket Duplicate() const
Definition: C4NetIO.h:324
C4TimeMilliseconds tLastPong
Definition: C4Network2IO.h:235
void HandlePuncherPacket(const C4NetIOPacket &rPacket)
bool InitPuncher(C4NetIO::addr_t PuncherAddr)
bool isAutoAccepted() const
Definition: C4Network2IO.h:289
const char * getNetIOName(C4NetIO *pNetIO)
bool IsPuncherAddr(const C4NetIO::addr_t &addr) const
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:260
bool isClosed() const
Definition: C4Network2IO.h:288
void SetAddr(const C4NetIO::addr_t &naddr)
Definition: C4NetIO.h:326
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:43
C4PacketBase * getPkt() const
Definition: C4PacketBase.h:255
virtual bool SetBroadcast(const addr_t &addr, bool fSet=true)=0
C4PacketType getPktType() const
Definition: C4PacketBase.h:254
Definition: C4Network2IO.h:414
C4Application Application
Definition: C4Globals.cpp:44
C4InteractiveThread InteractiveThread
Definition: C4Application.h:45
void unpack(const C4NetIOPacket &Pkt, char *pStatus=nullptr)
Definition: C4Packet2.cpp:159
static C4TimeMilliseconds Now()
void ClearPacketLog(uint32_t iStartNumber=~0)
void HandlePacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn)
Definition: C4Network2.cpp:878
bool isOpen() const
Definition: C4Network2IO.h:285
void SetReference(C4Network2Reference *pReference)
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
virtual bool Connect(const addr_t &addr)=0
void SetPeerAddr(const C4NetIO::addr_t &nPeerAddr)