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