| 1 | /* |
| 2 | * LegacyClonk |
| 3 | * |
| 4 | * Copyright (c) RedWolf Design |
| 5 | * Copyright (c) 2013-2018, The OpenClonk Team and contributors |
| 6 | * Copyright (c) 2017-2021, The LegacyClonk Team and contributors |
| 7 | * |
| 8 | * Distributed under the terms of the ISC license; see accompanying file |
| 9 | * "COPYING" for details. |
| 10 | * |
| 11 | * "Clonk" is a registered trademark of Matthes Bender, used with permission. |
| 12 | * See accompanying file "TRADEMARK" for details. |
| 13 | * |
| 14 | * To redistribute this file separately, substitute the full license texts |
| 15 | * for the above references. |
| 16 | */ |
| 17 | |
| 18 | #pragma once |
| 19 | |
| 20 | #include "C4Network2Address.h" |
| 21 | #include "C4NetIO.h" |
| 22 | #include "C4Client.h" |
| 23 | #include "C4InteractiveThread.h" |
| 24 | #include "C4Log.h" |
| 25 | #include "C4PuncherPacket.h" |
| 26 | |
| 27 | #include <atomic> |
| 28 | #include <cstdint> |
| 29 | |
| 30 | class C4Network2IOConnection; |
| 31 | |
| 32 | // constants |
| 33 | |
| 34 | const int C4NetTimer = 500, // ms |
| 35 | C4NetPingFreq = 1000, // ms |
| 36 | C4NetStatisticsFreq = 1000, // ms |
| 37 | C4NetAcceptTimeout = 10, // s |
| 38 | C4NetPingTimeout = 30000; // ms |
| 39 | |
| 40 | // client count |
| 41 | const int C4NetMaxClients = 256; |
| 42 | |
| 43 | class C4Network2IO |
| 44 | : protected C4InteractiveThread::Callback, |
| 45 | protected C4NetIO::CBClass, |
| 46 | protected StdSchedulerProc |
| 47 | { |
| 48 | public: |
| 49 | C4Network2IO(); |
| 50 | virtual ~C4Network2IO(); |
| 51 | |
| 52 | protected: |
| 53 | // main traffic net i/o classes |
| 54 | C4NetIO *pNetIO_TCP, *pNetIO_UDP; |
| 55 | |
| 56 | // discovery net i/o |
| 57 | class C4Network2IODiscover *pNetIODiscover; |
| 58 | |
| 59 | // UPnP |
| 60 | std::unique_ptr<class C4Network2UPnP> UPnP; |
| 61 | |
| 62 | // reference server |
| 63 | class C4Network2RefServer *pRefServer; |
| 64 | |
| 65 | // local client core |
| 66 | C4ClientCore LCCore; |
| 67 | CStdCSec LCCoreCSec; |
| 68 | |
| 69 | // connection list |
| 70 | C4Network2IOConnection *pConnList; |
| 71 | CStdCSec ConnListCSec, BroadcastCSec; |
| 72 | |
| 73 | // next connection ID to use |
| 74 | uint32_t iNextConnID; |
| 75 | |
| 76 | // allow incoming connections? |
| 77 | bool fAllowConnect; |
| 78 | |
| 79 | // connection acceptance |
| 80 | struct AutoAccept |
| 81 | { |
| 82 | C4ClientCore CCore; |
| 83 | AutoAccept *Next; |
| 84 | } |
| 85 | *pAutoAcceptList; |
| 86 | CStdCSec AutoAcceptCSec; |
| 87 | |
| 88 | // make sure only one connection is established? |
| 89 | bool fExclusiveConn; |
| 90 | |
| 91 | // timer & ping |
| 92 | unsigned long iLastExecute, iLastPing; |
| 93 | |
| 94 | // statistics |
| 95 | unsigned long iLastStatistic; |
| 96 | int iTCPIRate, iTCPORate, iTCPBCRate, |
| 97 | iUDPIRate, iUDPORate, iUDPBCRate; |
| 98 | |
| 99 | // punching |
| 100 | C4NetIO::addr_t PuncherAddrIPv4, PuncherAddrIPv6; |
| 101 | bool IsPuncherAddr(const C4NetIO::addr_t &addr) const; |
| 102 | |
| 103 | // logger |
| 104 | std::shared_ptr<spdlog::logger> logger; |
| 105 | |
| 106 | public: |
| 107 | bool hasTCP() const { return !!pNetIO_TCP; } |
| 108 | bool hasUDP() const { return !!pNetIO_UDP; } |
| 109 | |
| 110 | // initialization |
| 111 | bool Init(std::uint16_t iPortTCP, std::uint16_t iPortUDP, std::uint16_t iPortDiscovery = 0, std::uint16_t iPortRefServer = 0); // by main thread |
| 112 | void Clear(); // by main thread |
| 113 | void SetLocalCCore(const C4ClientCore &CCore); // by main thread |
| 114 | |
| 115 | // i/o types |
| 116 | C4NetIO *MsgIO(); // by both |
| 117 | C4NetIO *DataIO(); // by both |
| 118 | |
| 119 | // connections |
| 120 | bool Connect(const C4NetIO::addr_t &addr, C4Network2IOProtocol prot, const C4ClientCore &ccore, const char *password = nullptr); // by main thread |
| 121 | bool ConnectWithSocket(const C4NetIO::addr_t &addr, C4Network2IOProtocol eProt, const C4ClientCore &nCCore, std::unique_ptr<C4NetIOTCP::Socket> socket, const char *szPassword = nullptr); // by main thread |
| 122 | void SetAcceptMode(bool fAcceptAll); // by main thread |
| 123 | void SetExclusiveConnMode(bool fExclusiveConn); // by main thread |
| 124 | int getConnectionCount(); // by main thread |
| 125 | |
| 126 | void ClearAutoAccept(); // by main thread |
| 127 | void AddAutoAccept(const C4ClientCore &CCore); // by main thread |
| 128 | void RemoveAutoAccept(const C4ClientCore &CCore); // by main thread |
| 129 | |
| 130 | C4Network2IOConnection *GetMsgConnection(int iClientID); // by both (returns referenced connection!) |
| 131 | C4Network2IOConnection *GetDataConnection(int iClientID); // by both (returns referenced connection!) |
| 132 | |
| 133 | // broadcasting |
| 134 | void BeginBroadcast(bool fSelectAll = false); // by both |
| 135 | void EndBroadcast(); // by both |
| 136 | bool Broadcast(const C4NetIOPacket &rPkt); // by both |
| 137 | |
| 138 | // sending helpers |
| 139 | bool BroadcastMsg(const C4NetIOPacket &rPkt); // by both |
| 140 | |
| 141 | // punch |
| 142 | bool InitPuncher(C4NetIO::addr_t puncherAddr); // by main thread |
| 143 | void SendPuncherPacket(const C4NetpuncherPacket &, C4Network2HostAddress::AddressFamily family); |
| 144 | void Punch(const C4NetIO::addr_t &); // Sends a ping packet |
| 145 | |
| 146 | // stuff |
| 147 | C4NetIO *getNetIO(C4Network2IOProtocol eProt); // by both |
| 148 | const char *getNetIOName(C4NetIO *pNetIO); |
| 149 | C4Network2IOProtocol getNetIOProt(C4NetIO *pNetIO); |
| 150 | |
| 151 | // statistics |
| 152 | int getProtIRate (C4Network2IOProtocol eProt) const { return eProt == P_TCP ? iTCPIRate : iUDPIRate; } |
| 153 | int getProtORate (C4Network2IOProtocol eProt) const { return eProt == P_TCP ? iTCPORate : iUDPORate; } |
| 154 | int getProtBCRate(C4Network2IOProtocol eProt) const { return eProt == P_TCP ? iTCPBCRate : iUDPBCRate; } |
| 155 | |
| 156 | // reference |
| 157 | void SetReference(class C4Network2Reference *pReference); |
| 158 | bool IsReferenceNeeded(); |
| 159 | |
| 160 | protected: |
| 161 | // *** callbacks |
| 162 | // C4NetIO-Callbacks |
| 163 | virtual bool OnConn(const C4NetIO::addr_t &addr, const C4NetIO::addr_t &AddrConnect, const C4NetIO::addr_t *pOwnAddr, C4NetIO *pNetIO) override; |
| 164 | virtual void OnDisconn(const C4NetIO::addr_t &addr, C4NetIO *pNetIO, const char *szReason) override; |
| 165 | virtual void OnPacket(const C4NetIOPacket &rPacket, C4NetIO *pNetIO) override; |
| 166 | // StdSchedulerProc |
| 167 | virtual bool Execute(int iTimeout) override; |
| 168 | virtual int GetTimeout() override; |
| 169 | // Event callback by C4InteractiveThread |
| 170 | void OnThreadEvent(C4InteractiveEventType eEvent, const std::any &eventData) override; // by main thread |
| 171 | |
| 172 | // connections list |
| 173 | void AddConnection(C4Network2IOConnection *pConn); // by both |
| 174 | void RemoveConnection(C4Network2IOConnection *pConn); // by both |
| 175 | C4Network2IOConnection *GetConnection(const C4NetIO::addr_t &addr, C4NetIO *pNetIO); // by both |
| 176 | C4Network2IOConnection *GetConnectionByConnAddr(const C4NetIO::addr_t &addr, C4NetIO *pNetIO); // by both |
| 177 | C4Network2IOConnection *GetConnectionByID(uint32_t iConnID); // by thread |
| 178 | |
| 179 | // network events (signals to main thread) |
| 180 | struct NetEvPacketData; |
| 181 | |
| 182 | // connection acceptance |
| 183 | bool doAutoAccept(const C4ClientCore &CCore, const C4Network2IOConnection &Conn); |
| 184 | |
| 185 | // general packet handling (= forward in most cases) |
| 186 | bool HandlePacket(const C4NetIOPacket &rPacket, C4Network2IOConnection *pConn, bool fThread); // by both |
| 187 | void CallHandlers(int iHandlers, const class C4IDPacket *pPacket, C4Network2IOConnection *pConn, bool fThread); // by both |
| 188 | |
| 189 | // packet handling (some are really handled here) |
| 190 | void HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn); |
| 191 | void HandleFwdReq(const class C4PacketFwd &rFwd, C4Network2IOConnection *pBy); |
| 192 | void HandlePuncherPacket(const C4NetIOPacket &packet); |
| 193 | |
| 194 | // misc |
| 195 | bool Ping(); |
| 196 | void CheckTimeout(); |
| 197 | void GenerateStatistics(int iInterval); |
| 198 | void SendConnPackets(); |
| 199 | }; |
| 200 | |
| 201 | C4LOGGERCONFIG_NAME_TYPE(C4Network2IO); |
| 202 | |
| 203 | template<> |
| 204 | struct C4LoggerConfig::Defaults<C4Network2IO> |
| 205 | { |
| 206 | static constexpr spdlog::level::level_enum GuiLogLevel{spdlog::level::err}; |
| 207 | }; |
| 208 | |
| 209 | enum C4Network2IOConnStatus |
| 210 | { |
| 211 | CS_Connect, // waiting for connection |
| 212 | CS_Connected, // waiting for Conn |
| 213 | CS_HalfAccepted, // got Conn (peer identified, client class created if neccessary) |
| 214 | CS_Accepted, // got ConnRe (peer did accept) |
| 215 | CS_Closed, |
| 216 | CS_ConnectFail, // got closed before HalfAccepted was reached |
| 217 | }; |
| 218 | |
| 219 | class C4Network2IOConnection // shared |
| 220 | { |
| 221 | friend class C4Network2IO; |
| 222 | |
| 223 | public: |
| 224 | C4Network2IOConnection(); |
| 225 | ~C4Network2IOConnection(); |
| 226 | |
| 227 | protected: |
| 228 | // connection data |
| 229 | class C4NetIO *pNetClass; |
| 230 | C4Network2IOProtocol eProt; |
| 231 | C4NetIO::addr_t PeerAddr, ConnectAddr; |
| 232 | std::unique_ptr<C4NetIOTCP::Socket> tcpSimOpenSocket; |
| 233 | |
| 234 | // status data |
| 235 | C4Network2IOConnStatus Status; |
| 236 | uint32_t iID, iRemoteID; // connection ID for this and the remote client |
| 237 | bool fAutoAccept; // auto accepted by thread? |
| 238 | bool fBroadcastTarget; // broadcast target? |
| 239 | time_t iTimestamp; // timestamp of last status change |
| 240 | int iPingTime; // ping |
| 241 | unsigned long iLastPing; // if > iLastPong, it's the first ping that hasn't been answered yet |
| 242 | unsigned long iLastPong; // last pong received |
| 243 | C4ClientCore CCore; // client core (>= CS_HalfAccepted) |
| 244 | CStdCSec CCoreCSec; |
| 245 | int iIRate, iORate; // input/output rates (by C4NetIO, in b/s) |
| 246 | int iPacketLoss; // lost packets (in the last seconds) |
| 247 | StdStrBuf Password; // password to use for connect |
| 248 | bool fConnSent; // initial connection packet send |
| 249 | bool fPostMortemSent; // post mortem send |
| 250 | |
| 251 | // packet backlog |
| 252 | uint32_t iOutPacketCounter, iInPacketCounter; |
| 253 | struct PacketLogEntry |
| 254 | { |
| 255 | uint32_t Number; |
| 256 | C4NetIOPacket Pkt; |
| 257 | PacketLogEntry *Next; |
| 258 | }; |
| 259 | PacketLogEntry *pPacketLog; |
| 260 | CStdCSec PacketLogCSec; |
| 261 | |
| 262 | // list (C4Network2IO) |
| 263 | C4Network2IOConnection *pNext; |
| 264 | |
| 265 | // reference counter |
| 266 | std::atomic<long> iRefCnt; |
| 267 | |
| 268 | public: |
| 269 | C4NetIO *getNetClass() const { return pNetClass; } |
| 270 | C4Network2IOProtocol getProtocol() const { return eProt; } |
| 271 | const C4NetIO::addr_t &getPeerAddr() const { return PeerAddr.GetPort() ? PeerAddr : ConnectAddr; } |
| 272 | const C4NetIO::addr_t &getConnectAddr() const { return ConnectAddr; } |
| 273 | uint32_t getID() const { return iID; } |
| 274 | time_t getTimestamp() const { return iTimestamp; } |
| 275 | const C4ClientCore &getCCore() const { return CCore; } |
| 276 | int getClientID() const { return CCore.getID(); } |
| 277 | bool isHost() const { return CCore.isHost(); } |
| 278 | int getPingTime() const { return iPingTime; } |
| 279 | int getLag() const; |
| 280 | int getPacketLoss() const { return iPacketLoss; } |
| 281 | const char *getPassword() const { return Password.getData(); } |
| 282 | bool isConnSent() const { return fConnSent; } |
| 283 | |
| 284 | uint32_t getInPacketCounter() const { return iInPacketCounter; } |
| 285 | uint32_t getOutPacketCounter() const { return iOutPacketCounter; } |
| 286 | |
| 287 | bool isConnecting() const { return Status == CS_Connect; } |
| 288 | bool isOpen() const { return Status != CS_Connect && Status != CS_Closed && Status != CS_ConnectFail; } |
| 289 | bool isHalfAccepted() const { return Status == CS_HalfAccepted || Status == CS_Accepted; } |
| 290 | bool isAccepted() const { return Status == CS_Accepted; } |
| 291 | bool isClosed() const { return Status == CS_Closed || Status == CS_ConnectFail; } |
| 292 | bool isAutoAccepted() const { return fAutoAccept; } |
| 293 | bool isBroadcastTarget() const { return fBroadcastTarget; } |
| 294 | bool isFailed() const { return Status == CS_ConnectFail; } |
| 295 | |
| 296 | protected: |
| 297 | // called by C4Network2IO only |
| 298 | void Set(C4NetIO *pnNetClass, C4Network2IOProtocol eProt, const C4NetIO::addr_t &nPeerAddr, const C4NetIO::addr_t &nConnectAddr, C4Network2IOConnStatus nStatus, const char *szPassword, uint32_t iID); |
| 299 | void SetSocket(std::unique_ptr<C4NetIOTCP::Socket> socket); |
| 300 | void SetRemoteID(uint32_t iRemoteID); |
| 301 | void SetPeerAddr(const C4NetIO::addr_t &nPeerAddr); |
| 302 | void OnPing(); |
| 303 | void SetPingTime(int iPingTime); |
| 304 | void SetStatus(C4Network2IOConnStatus nStatus); |
| 305 | void SetAutoAccepted(); |
| 306 | void OnPacketReceived(uint8_t iPacketType); |
| 307 | void ClearPacketLog(uint32_t iStartNumber = ~0); |
| 308 | |
| 309 | public: |
| 310 | // status changing |
| 311 | void SetHalfAccepted() { SetStatus(CS_HalfAccepted); } |
| 312 | void SetAccepted() { SetStatus(CS_Accepted); } |
| 313 | void SetCCore(const C4ClientCore &nCCore); |
| 314 | void ResetAutoAccepted() { fAutoAccept = false; } |
| 315 | void SetConnSent() { fConnSent = true; } |
| 316 | |
| 317 | // connection operations |
| 318 | bool Connect(); |
| 319 | void Close(); |
| 320 | bool Send(const C4NetIOPacket &rPkt); |
| 321 | void SetBroadcastTarget(bool fSet); // (only call after C4Network2IO::BeginBroadcast!) |
| 322 | |
| 323 | // statistics |
| 324 | void DoStatistics(int iInterval, int *pIRateSum, int *pORateSum); |
| 325 | |
| 326 | // reference counting |
| 327 | void AddRef(); void DelRef(); |
| 328 | |
| 329 | // post mortem |
| 330 | bool CreatePostMortem(class C4PacketPostMortem *pPkt); |
| 331 | }; |
| 332 | |
| 333 | // Packets |
| 334 | |
| 335 | class C4PacketPing : public C4PacketBase |
| 336 | { |
| 337 | public: |
| 338 | C4PacketPing(uint32_t iPacketCounter = 0); |
| 339 | |
| 340 | protected: |
| 341 | uint32_t iTime; |
| 342 | uint32_t iPacketCounter; |
| 343 | |
| 344 | public: |
| 345 | uint32_t getTravelTime() const; |
| 346 | uint32_t getPacketCounter() const { return iPacketCounter; } |
| 347 | |
| 348 | virtual void CompileFunc(StdCompiler *pComp) override; |
| 349 | }; |
| 350 | |
| 351 | class C4PacketConn : public C4PacketBase |
| 352 | { |
| 353 | public: |
| 354 | C4PacketConn(); |
| 355 | C4PacketConn(const class C4ClientCore &nCCore, uint32_t iConnID, const char *szPassword = nullptr); |
| 356 | |
| 357 | protected: |
| 358 | int32_t iVer; |
| 359 | uint32_t iConnID; |
| 360 | C4ClientCore CCore; |
| 361 | StdStrBuf Password; |
| 362 | |
| 363 | public: |
| 364 | int32_t getVer() const { return iVer; } |
| 365 | uint32_t getConnID() const { return iConnID; } |
| 366 | const C4ClientCore &getCCore() const { return CCore; } |
| 367 | const char *getPassword() const { return Password.getData(); } |
| 368 | |
| 369 | virtual void CompileFunc(StdCompiler *pComp) override; |
| 370 | }; |
| 371 | |
| 372 | class C4PacketConnRe : public C4PacketBase |
| 373 | { |
| 374 | public: |
| 375 | C4PacketConnRe(); |
| 376 | C4PacketConnRe(bool fOK, bool fWrongPassword, const char *szMsg = nullptr); |
| 377 | |
| 378 | protected: |
| 379 | bool fOK, fWrongPassword; |
| 380 | StdStrBuf szMsg; |
| 381 | |
| 382 | public: |
| 383 | bool isOK() const { return fOK; } |
| 384 | bool isPasswordWrong() const { return fWrongPassword; } |
| 385 | const char *getMsg() const { return szMsg.getData(); } |
| 386 | |
| 387 | virtual void CompileFunc(StdCompiler *pComp) override; |
| 388 | }; |
| 389 | |
| 390 | class C4PacketFwd : public C4PacketBase |
| 391 | { |
| 392 | public: |
| 393 | C4PacketFwd(); |
| 394 | |
| 395 | protected: |
| 396 | bool fNegativeList; |
| 397 | int32_t iClients[C4NetMaxClients]; |
| 398 | int32_t iClientCnt; |
| 399 | C4NetIOPacket Data; |
| 400 | |
| 401 | public: |
| 402 | const C4NetIOPacket &getData() const { return Data; } |
| 403 | int32_t getClient(int32_t i) const { return iClients[i]; } |
| 404 | int32_t getClientCnt() const { return iClientCnt; } |
| 405 | |
| 406 | bool DoFwdTo(int32_t iClient) const; |
| 407 | |
| 408 | void SetData(const C4NetIOPacket &Pkt); |
| 409 | void SetListType(bool fnNegativeList); |
| 410 | void AddClient(int32_t iClient); |
| 411 | |
| 412 | virtual void CompileFunc(StdCompiler *pComp) override; |
| 413 | }; |
| 414 | |
| 415 | class C4PacketPostMortem : public C4PacketBase |
| 416 | { |
| 417 | public: |
| 418 | C4PacketPostMortem(); |
| 419 | ~C4PacketPostMortem(); |
| 420 | |
| 421 | private: |
| 422 | uint32_t iConnID; |
| 423 | uint32_t iPacketCounter; // last packet counter of dead connection |
| 424 | uint32_t iPacketCount; |
| 425 | struct PacketLink |
| 426 | { |
| 427 | C4NetIOPacket Pkt; |
| 428 | PacketLink *Next; |
| 429 | }; |
| 430 | PacketLink *pPackets; |
| 431 | |
| 432 | public: |
| 433 | const uint32_t getConnID() const { return iConnID; } |
| 434 | const uint32_t getPacketCount() const { return iPacketCount; } |
| 435 | void SetConnID(uint32_t inConnID) { iConnID = inConnID; } |
| 436 | |
| 437 | const C4NetIOPacket *getPacket(uint32_t iNumber) const; |
| 438 | void SetPacketCounter(uint32_t iPacketCounter); |
| 439 | void Add(const C4NetIOPacket &rPkt); |
| 440 | |
| 441 | virtual void CompileFunc(StdCompiler *pComp) override; |
| 442 | }; |
| 443 | |