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